VirtualBox

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

Last change on this file since 36131 was 35781, checked in by vboxsync, 14 years ago

Storage/VD: Use the correct image for writing, fixes VERR_VD_READONLY errors when using async I/O + snapshots

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 291.7 KB
Line 
1/* $Id: VD.cpp 35781 2011-01-31 11:13:53Z vboxsync $ */
2/** @file
3 * VBoxHDD - VBox HDD Container implementation.
4 */
5
6/*
7 * Copyright (C) 2006-2010 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* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_VD
22#include <VBox/vd.h>
23#include <VBox/err.h>
24#include <VBox/sup.h>
25#include <VBox/log.h>
26
27#include <iprt/alloc.h>
28#include <iprt/assert.h>
29#include <iprt/uuid.h>
30#include <iprt/file.h>
31#include <iprt/string.h>
32#include <iprt/asm.h>
33#include <iprt/ldr.h>
34#include <iprt/dir.h>
35#include <iprt/path.h>
36#include <iprt/param.h>
37#include <iprt/memcache.h>
38#include <iprt/sg.h>
39#include <iprt/critsect.h>
40#include <iprt/list.h>
41#include <iprt/avl.h>
42
43#include <VBox/vd-plugin.h>
44#include <VBox/vd-cache-plugin.h>
45
46#define VBOXHDDDISK_SIGNATURE 0x6f0e2a7d
47
48/** Buffer size used for merging images. */
49#define VD_MERGE_BUFFER_SIZE (16 * _1M)
50
51/** Maximum number of segments in one I/O task. */
52#define VD_IO_TASK_SEGMENTS_MAX 64
53
54/**
55 * VD async I/O interface storage descriptor.
56 */
57typedef struct VDIIOFALLBACKSTORAGE
58{
59 /** File handle. */
60 RTFILE File;
61 /** Completion callback. */
62 PFNVDCOMPLETED pfnCompleted;
63 /** Thread for async access. */
64 RTTHREAD ThreadAsync;
65} VDIIOFALLBACKSTORAGE, *PVDIIOFALLBACKSTORAGE;
66
67/**
68 * Structure containing everything I/O related
69 * for the image and cache descriptors.
70 */
71typedef struct VDIO
72{
73 /** I/O interface to the upper layer. */
74 PVDINTERFACE pInterfaceIO;
75 /** I/O interface callback table. */
76 PVDINTERFACEIO pInterfaceIOCallbacks;
77
78 /** Per image internal I/O interface. */
79 VDINTERFACE VDIIOInt;
80
81 /** Fallback I/O interface, only used if the caller doesn't provide it. */
82 VDINTERFACE VDIIO;
83
84 /** Opaque backend data. */
85 void *pBackendData;
86 /** Disk this image is part of */
87 PVBOXHDD pDisk;
88} VDIO, *PVDIO;
89
90/**
91 * VBox HDD Container image descriptor.
92 */
93typedef struct VDIMAGE
94{
95 /** Link to parent image descriptor, if any. */
96 struct VDIMAGE *pPrev;
97 /** Link to child image descriptor, if any. */
98 struct VDIMAGE *pNext;
99 /** Container base filename. (UTF-8) */
100 char *pszFilename;
101 /** Data managed by the backend which keeps the actual info. */
102 void *pBackendData;
103 /** Cached sanitized image flags. */
104 unsigned uImageFlags;
105 /** Image open flags (only those handled generically in this code and which
106 * the backends will never ever see). */
107 unsigned uOpenFlags;
108
109 /** Function pointers for the various backend methods. */
110 PCVBOXHDDBACKEND Backend;
111 /** Pointer to list of VD interfaces, per-image. */
112 PVDINTERFACE pVDIfsImage;
113 /** I/O related things. */
114 VDIO VDIo;
115} VDIMAGE, *PVDIMAGE;
116
117/**
118 * uModified bit flags.
119 */
120#define VD_IMAGE_MODIFIED_FLAG RT_BIT(0)
121#define VD_IMAGE_MODIFIED_FIRST RT_BIT(1)
122#define VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE RT_BIT(2)
123
124
125/**
126 * VBox HDD Cache image descriptor.
127 */
128typedef struct VDCACHE
129{
130 /** Cache base filename. (UTF-8) */
131 char *pszFilename;
132 /** Data managed by the backend which keeps the actual info. */
133 void *pBackendData;
134 /** Cached sanitized image flags. */
135 unsigned uImageFlags;
136 /** Image open flags (only those handled generically in this code and which
137 * the backends will never ever see). */
138 unsigned uOpenFlags;
139
140 /** Function pointers for the various backend methods. */
141 PCVDCACHEBACKEND Backend;
142
143 /** Pointer to list of VD interfaces, per-cache. */
144 PVDINTERFACE pVDIfsCache;
145 /** I/O related things. */
146 VDIO VDIo;
147} VDCACHE, *PVDCACHE;
148
149/**
150 * VBox HDD Container main structure, private part.
151 */
152struct VBOXHDD
153{
154 /** Structure signature (VBOXHDDDISK_SIGNATURE). */
155 uint32_t u32Signature;
156
157 /** Image type. */
158 VDTYPE enmType;
159
160 /** Number of opened images. */
161 unsigned cImages;
162
163 /** Base image. */
164 PVDIMAGE pBase;
165
166 /** Last opened image in the chain.
167 * The same as pBase if only one image is used. */
168 PVDIMAGE pLast;
169
170 /** If a merge to one of the parents is running this may be non-NULL
171 * to indicate to what image the writes should be additionally relayed. */
172 PVDIMAGE pImageRelay;
173
174 /** Flags representing the modification state. */
175 unsigned uModified;
176
177 /** Cached size of this disk. */
178 uint64_t cbSize;
179 /** Cached PCHS geometry for this disk. */
180 VDGEOMETRY PCHSGeometry;
181 /** Cached LCHS geometry for this disk. */
182 VDGEOMETRY LCHSGeometry;
183
184 /** Pointer to list of VD interfaces, per-disk. */
185 PVDINTERFACE pVDIfsDisk;
186 /** Pointer to the common interface structure for error reporting. */
187 PVDINTERFACE pInterfaceError;
188 /** Pointer to the error interface callbacks we use if available. */
189 PVDINTERFACEERROR pInterfaceErrorCallbacks;
190
191 /** Pointer to the optional thread synchronization interface. */
192 PVDINTERFACE pInterfaceThreadSync;
193 /** Pointer to the optional thread synchronization callbacks. */
194 PVDINTERFACETHREADSYNC pInterfaceThreadSyncCallbacks;
195
196 /** Internal I/O interface callback table for the images. */
197 VDINTERFACEIOINT VDIIOIntCallbacks;
198
199 /** Callback table for the fallback I/O interface. */
200 VDINTERFACEIO VDIIOCallbacks;
201
202 /** Memory cache for I/O contexts */
203 RTMEMCACHE hMemCacheIoCtx;
204 /** Memory cache for I/O tasks. */
205 RTMEMCACHE hMemCacheIoTask;
206 /** Critical section protecting the disk against concurrent access. */
207 RTCRITSECT CritSect;
208 /** Flag whether the disk is currently locked by growing write or a flush
209 * request. Other flush or growing write requests need to wait until
210 * the current one completes.
211 */
212 volatile bool fLocked;
213 /** List of waiting requests. - Protected by the critical section. */
214 RTLISTNODE ListWriteLocked;
215
216 /** Pointer to the L2 disk cache if any. */
217 PVDCACHE pCache;
218};
219
220# define VD_THREAD_IS_CRITSECT_OWNER(Disk) \
221 do \
222 { \
223 AssertMsg(RTCritSectIsOwner(&Disk->CritSect), \
224 ("Thread does not own critical section\n"));\
225 } while(0)
226
227/**
228 * VBox parent read descriptor, used internally for compaction.
229 */
230typedef struct VDPARENTSTATEDESC
231{
232 /** Pointer to disk descriptor. */
233 PVBOXHDD pDisk;
234 /** Pointer to image descriptor. */
235 PVDIMAGE pImage;
236} VDPARENTSTATEDESC, *PVDPARENTSTATEDESC;
237
238/**
239 * Transfer direction.
240 */
241typedef enum VDIOCTXTXDIR
242{
243 /** Read */
244 VDIOCTXTXDIR_READ = 0,
245 /** Write */
246 VDIOCTXTXDIR_WRITE,
247 /** Flush */
248 VDIOCTXTXDIR_FLUSH,
249 /** 32bit hack */
250 VDIOCTXTXDIR_32BIT_HACK = 0x7fffffff
251} VDIOCTXTXDIR, *PVDIOCTXTXDIR;
252
253/** Transfer function */
254typedef DECLCALLBACK(int) FNVDIOCTXTRANSFER (PVDIOCTX pIoCtx);
255/** Pointer to a transfer function. */
256typedef FNVDIOCTXTRANSFER *PFNVDIOCTXTRANSFER;
257
258/**
259 * I/O context
260 */
261typedef struct VDIOCTX
262{
263 /** Disk this is request is for. */
264 PVBOXHDD pDisk;
265 /** Return code. */
266 int rcReq;
267 /** Transfer direction */
268 VDIOCTXTXDIR enmTxDir;
269 /** Number of bytes left until this context completes. */
270 volatile uint32_t cbTransferLeft;
271 /** Current offset */
272 volatile uint64_t uOffset;
273 /** Number of bytes to transfer */
274 volatile size_t cbTransfer;
275 /** Current image in the chain. */
276 PVDIMAGE pImageCur;
277 /** Start image to read from. pImageCur is reset to this
278 * value after it reached the first image in the chain. */
279 PVDIMAGE pImageStart;
280 /** S/G buffer */
281 RTSGBUF SgBuf;
282 /** Flag whether the I/O context is blocked because it is in the growing list. */
283 bool fBlocked;
284 /** Number of data transfers currently pending. */
285 volatile uint32_t cDataTransfersPending;
286 /** How many meta data transfers are pending. */
287 volatile uint32_t cMetaTransfersPending;
288 /** Flag whether the request finished */
289 volatile bool fComplete;
290 /** Temporary allocated memory which is freed
291 * when the context completes. */
292 void *pvAllocation;
293 /** Transfer function. */
294 PFNVDIOCTXTRANSFER pfnIoCtxTransfer;
295 /** Next transfer part after the current one completed. */
296 PFNVDIOCTXTRANSFER pfnIoCtxTransferNext;
297 /** Parent I/O context if any. Sets the type of the context (root/child) */
298 PVDIOCTX pIoCtxParent;
299 /** Type dependent data (root/child) */
300 union
301 {
302 /** Root data */
303 struct
304 {
305 /** Completion callback */
306 PFNVDASYNCTRANSFERCOMPLETE pfnComplete;
307 /** User argument 1 passed on completion. */
308 void *pvUser1;
309 /** User argument 1 passed on completion. */
310 void *pvUser2;
311 } Root;
312 /** Child data */
313 struct
314 {
315 /** Saved start offset */
316 uint64_t uOffsetSaved;
317 /** Saved transfer size */
318 size_t cbTransferLeftSaved;
319 /** Number of bytes transferred from the parent if this context completes. */
320 size_t cbTransferParent;
321 /** Number of bytes to pre read */
322 size_t cbPreRead;
323 /** Number of bytes to post read. */
324 size_t cbPostRead;
325 /** Number of bytes to write left in the parent. */
326 size_t cbWriteParent;
327 /** Write type dependent data. */
328 union
329 {
330 /** Optimized */
331 struct
332 {
333 /** Bytes to fill to satisfy the block size. Not part of the virtual disk. */
334 size_t cbFill;
335 /** Bytes to copy instead of reading from the parent */
336 size_t cbWriteCopy;
337 /** Bytes to read from the image. */
338 size_t cbReadImage;
339 } Optimized;
340 } Write;
341 } Child;
342 } Type;
343} VDIOCTX;
344
345typedef struct VDIOCTXDEFERRED
346{
347 /** Node in the list of deferred requests.
348 * A request can be deferred if the image is growing
349 * and the request accesses the same range or if
350 * the backend needs to read or write metadata from the disk
351 * before it can continue. */
352 RTLISTNODE NodeDeferred;
353 /** I/O context this entry points to. */
354 PVDIOCTX pIoCtx;
355} VDIOCTXDEFERRED, *PVDIOCTXDEFERRED;
356
357/**
358 * I/O task.
359 */
360typedef struct VDIOTASK
361{
362 /** Storage this task belongs to. */
363 PVDIOSTORAGE pIoStorage;
364 /** Optional completion callback. */
365 PFNVDXFERCOMPLETED pfnComplete;
366 /** Opaque user data. */
367 void *pvUser;
368 /** Flag whether this is a meta data transfer. */
369 bool fMeta;
370 /** Type dependent data. */
371 union
372 {
373 /** User data transfer. */
374 struct
375 {
376 /** Number of bytes this task transferred. */
377 uint32_t cbTransfer;
378 /** Pointer to the I/O context the task belongs. */
379 PVDIOCTX pIoCtx;
380 } User;
381 /** Meta data transfer. */
382 struct
383 {
384 /** Meta transfer this task is for. */
385 PVDMETAXFER pMetaXfer;
386 } Meta;
387 } Type;
388} VDIOTASK, *PVDIOTASK;
389
390/**
391 * Storage handle.
392 */
393typedef struct VDIOSTORAGE
394{
395 /** Image I/O state this storage handle belongs to. */
396 PVDIO pVDIo;
397 /** AVL tree for pending async metadata transfers. */
398 PAVLRFOFFTREE pTreeMetaXfers;
399 /** Storage handle */
400 void *pStorage;
401} VDIOSTORAGE;
402
403/**
404 * Metadata transfer.
405 *
406 * @note This entry can't be freed if either the list is not empty or
407 * the reference counter is not 0.
408 * The assumption is that the backends don't need to read huge amounts of
409 * metadata to complete a transfer so the additional memory overhead should
410 * be relatively small.
411 */
412typedef struct VDMETAXFER
413{
414 /** AVL core for fast search (the file offset is the key) */
415 AVLRFOFFNODECORE Core;
416 /** I/O storage for this transfer. */
417 PVDIOSTORAGE pIoStorage;
418 /** Flags. */
419 uint32_t fFlags;
420 /** List of I/O contexts waiting for this metadata transfer to complete. */
421 RTLISTNODE ListIoCtxWaiting;
422 /** Number of references to this entry. */
423 unsigned cRefs;
424 /** Size of the data stored with this entry. */
425 size_t cbMeta;
426 /** Data stored - variable size. */
427 uint8_t abData[1];
428} VDMETAXFER;
429
430/**
431 * The transfer direction for the metadata.
432 */
433#define VDMETAXFER_TXDIR_MASK 0x3
434#define VDMETAXFER_TXDIR_NONE 0x0
435#define VDMETAXFER_TXDIR_WRITE 0x1
436#define VDMETAXFER_TXDIR_READ 0x2
437#define VDMETAXFER_TXDIR_FLUSH 0x3
438#define VDMETAXFER_TXDIR_GET(flags) ((flags) & VDMETAXFER_TXDIR_MASK)
439#define VDMETAXFER_TXDIR_SET(flags, dir) ((flags) = (flags & ~VDMETAXFER_TXDIR_MASK) | (dir))
440
441extern VBOXHDDBACKEND g_RawBackend;
442extern VBOXHDDBACKEND g_VmdkBackend;
443extern VBOXHDDBACKEND g_VDIBackend;
444extern VBOXHDDBACKEND g_VhdBackend;
445extern VBOXHDDBACKEND g_ParallelsBackend;
446extern VBOXHDDBACKEND g_DmgBackend;
447extern VBOXHDDBACKEND g_ISCSIBackend;
448
449static unsigned g_cBackends = 0;
450static PVBOXHDDBACKEND *g_apBackends = NULL;
451static PVBOXHDDBACKEND aStaticBackends[] =
452{
453 &g_VmdkBackend,
454 &g_VDIBackend,
455 &g_VhdBackend,
456 &g_ParallelsBackend,
457 &g_DmgBackend,
458 &g_RawBackend,
459 &g_ISCSIBackend
460};
461
462/**
463 * Supported backends for the disk cache.
464 */
465extern VDCACHEBACKEND g_VciCacheBackend;
466
467static unsigned g_cCacheBackends = 0;
468static PVDCACHEBACKEND *g_apCacheBackends = NULL;
469static PVDCACHEBACKEND aStaticCacheBackends[] =
470{
471 &g_VciCacheBackend
472};
473
474/**
475 * internal: add several backends.
476 */
477static int vdAddBackends(PVBOXHDDBACKEND *ppBackends, unsigned cBackends)
478{
479 PVBOXHDDBACKEND *pTmp = (PVBOXHDDBACKEND*)RTMemRealloc(g_apBackends,
480 (g_cBackends + cBackends) * sizeof(PVBOXHDDBACKEND));
481 if (RT_UNLIKELY(!pTmp))
482 return VERR_NO_MEMORY;
483 g_apBackends = pTmp;
484 memcpy(&g_apBackends[g_cBackends], ppBackends, cBackends * sizeof(PVBOXHDDBACKEND));
485 g_cBackends += cBackends;
486 return VINF_SUCCESS;
487}
488
489/**
490 * internal: add single backend.
491 */
492DECLINLINE(int) vdAddBackend(PVBOXHDDBACKEND pBackend)
493{
494 return vdAddBackends(&pBackend, 1);
495}
496
497/**
498 * internal: add several cache backends.
499 */
500static int vdAddCacheBackends(PVDCACHEBACKEND *ppBackends, unsigned cBackends)
501{
502 PVDCACHEBACKEND *pTmp = (PVDCACHEBACKEND*)RTMemRealloc(g_apCacheBackends,
503 (g_cCacheBackends + cBackends) * sizeof(PVDCACHEBACKEND));
504 if (RT_UNLIKELY(!pTmp))
505 return VERR_NO_MEMORY;
506 g_apCacheBackends = pTmp;
507 memcpy(&g_apCacheBackends[g_cCacheBackends], ppBackends, cBackends * sizeof(PVDCACHEBACKEND));
508 g_cCacheBackends += cBackends;
509 return VINF_SUCCESS;
510}
511
512/**
513 * internal: add single cache backend.
514 */
515DECLINLINE(int) vdAddCacheBackend(PVDCACHEBACKEND pBackend)
516{
517 return vdAddCacheBackends(&pBackend, 1);
518}
519
520/**
521 * internal: issue error message.
522 */
523static int vdError(PVBOXHDD pDisk, int rc, RT_SRC_POS_DECL,
524 const char *pszFormat, ...)
525{
526 va_list va;
527 va_start(va, pszFormat);
528 if (pDisk->pInterfaceErrorCallbacks)
529 pDisk->pInterfaceErrorCallbacks->pfnError(pDisk->pInterfaceError->pvUser, rc, RT_SRC_POS_ARGS, pszFormat, va);
530 va_end(va);
531 return rc;
532}
533
534/**
535 * internal: thread synchronization, start read.
536 */
537DECLINLINE(int) vdThreadStartRead(PVBOXHDD pDisk)
538{
539 int rc = VINF_SUCCESS;
540 if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
541 rc = pDisk->pInterfaceThreadSyncCallbacks->pfnStartRead(pDisk->pInterfaceThreadSync->pvUser);
542 return rc;
543}
544
545/**
546 * internal: thread synchronization, finish read.
547 */
548DECLINLINE(int) vdThreadFinishRead(PVBOXHDD pDisk)
549{
550 int rc = VINF_SUCCESS;
551 if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
552 rc = pDisk->pInterfaceThreadSyncCallbacks->pfnFinishRead(pDisk->pInterfaceThreadSync->pvUser);
553 return rc;
554}
555
556/**
557 * internal: thread synchronization, start write.
558 */
559DECLINLINE(int) vdThreadStartWrite(PVBOXHDD pDisk)
560{
561 int rc = VINF_SUCCESS;
562 if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
563 rc = pDisk->pInterfaceThreadSyncCallbacks->pfnStartWrite(pDisk->pInterfaceThreadSync->pvUser);
564 return rc;
565}
566
567/**
568 * internal: thread synchronization, finish write.
569 */
570DECLINLINE(int) vdThreadFinishWrite(PVBOXHDD pDisk)
571{
572 int rc = VINF_SUCCESS;
573 if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
574 rc = pDisk->pInterfaceThreadSyncCallbacks->pfnFinishWrite(pDisk->pInterfaceThreadSync->pvUser);
575 return rc;
576}
577
578/**
579 * internal: find image format backend.
580 */
581static int vdFindBackend(const char *pszBackend, PCVBOXHDDBACKEND *ppBackend)
582{
583 int rc = VINF_SUCCESS;
584 PCVBOXHDDBACKEND pBackend = NULL;
585
586 if (!g_apBackends)
587 VDInit();
588
589 for (unsigned i = 0; i < g_cBackends; i++)
590 {
591 if (!RTStrICmp(pszBackend, g_apBackends[i]->pszBackendName))
592 {
593 pBackend = g_apBackends[i];
594 break;
595 }
596 }
597 *ppBackend = pBackend;
598 return rc;
599}
600
601/**
602 * internal: find cache format backend.
603 */
604static int vdFindCacheBackend(const char *pszBackend, PCVDCACHEBACKEND *ppBackend)
605{
606 int rc = VINF_SUCCESS;
607 PCVDCACHEBACKEND pBackend = NULL;
608
609 if (!g_apCacheBackends)
610 VDInit();
611
612 for (unsigned i = 0; i < g_cCacheBackends; i++)
613 {
614 if (!RTStrICmp(pszBackend, g_apCacheBackends[i]->pszBackendName))
615 {
616 pBackend = g_apCacheBackends[i];
617 break;
618 }
619 }
620 *ppBackend = pBackend;
621 return rc;
622}
623
624/**
625 * internal: add image structure to the end of images list.
626 */
627static void vdAddImageToList(PVBOXHDD pDisk, PVDIMAGE pImage)
628{
629 pImage->pPrev = NULL;
630 pImage->pNext = NULL;
631
632 if (pDisk->pBase)
633 {
634 Assert(pDisk->cImages > 0);
635 pImage->pPrev = pDisk->pLast;
636 pDisk->pLast->pNext = pImage;
637 pDisk->pLast = pImage;
638 }
639 else
640 {
641 Assert(pDisk->cImages == 0);
642 pDisk->pBase = pImage;
643 pDisk->pLast = pImage;
644 }
645
646 pDisk->cImages++;
647}
648
649/**
650 * internal: remove image structure from the images list.
651 */
652static void vdRemoveImageFromList(PVBOXHDD pDisk, PVDIMAGE pImage)
653{
654 Assert(pDisk->cImages > 0);
655
656 if (pImage->pPrev)
657 pImage->pPrev->pNext = pImage->pNext;
658 else
659 pDisk->pBase = pImage->pNext;
660
661 if (pImage->pNext)
662 pImage->pNext->pPrev = pImage->pPrev;
663 else
664 pDisk->pLast = pImage->pPrev;
665
666 pImage->pPrev = NULL;
667 pImage->pNext = NULL;
668
669 pDisk->cImages--;
670}
671
672/**
673 * internal: find image by index into the images list.
674 */
675static PVDIMAGE vdGetImageByNumber(PVBOXHDD pDisk, unsigned nImage)
676{
677 PVDIMAGE pImage = pDisk->pBase;
678 if (nImage == VD_LAST_IMAGE)
679 return pDisk->pLast;
680 while (pImage && nImage)
681 {
682 pImage = pImage->pNext;
683 nImage--;
684 }
685 return pImage;
686}
687
688/**
689 * Internal: Tries to read the desired range from the given cache.
690 *
691 * @returns VBox status code.
692 * @retval VERR_VD_BLOCK_FREE if the block is not in the cache.
693 * pcbRead will be set to the number of bytes not in the cache.
694 * Everything thereafter might be in the cache.
695 * @param pCache The cache to read from.
696 * @param uOffset Offset of the virtual disk to read.
697 * @param pvBuf Where to store the read data.
698 * @param cbRead How much to read.
699 * @param pcbRead Where to store the number of bytes actually read.
700 * On success this indicates the number of bytes read from the cache.
701 * If VERR_VD_BLOCK_FREE is returned this gives the number of bytes
702 * which are not in the cache.
703 * In both cases everything beyond this value
704 * might or might not be in the cache.
705 */
706static int vdCacheReadHelper(PVDCACHE pCache, uint64_t uOffset,
707 void *pvBuf, size_t cbRead, size_t *pcbRead)
708{
709 int rc = VINF_SUCCESS;
710
711 LogFlowFunc(("pCache=%#p uOffset=%llu pvBuf=%#p cbRead=%zu pcbRead=%#p\n",
712 pCache, uOffset, pvBuf, cbRead, pcbRead));
713
714 AssertPtr(pCache);
715 AssertPtr(pcbRead);
716
717 rc = pCache->Backend->pfnRead(pCache->pBackendData, uOffset, pvBuf,
718 cbRead, pcbRead);
719
720 LogFlowFunc(("returns rc=%Rrc pcbRead=%zu\n", rc, *pcbRead));
721 return rc;
722}
723
724/**
725 * Internal: Writes data for the given block into the cache.
726 *
727 * @returns VBox status code.
728 * @param pCache The cache to write to.
729 * @param uOffset Offset of the virtual disk to write to teh cache.
730 * @param pcvBuf The data to write.
731 * @param cbWrite How much to write.
732 * @param pcbWritten How much data could be written, optional.
733 */
734static int vdCacheWriteHelper(PVDCACHE pCache, uint64_t uOffset, const void *pcvBuf,
735 size_t cbWrite, size_t *pcbWritten)
736{
737 int rc = VINF_SUCCESS;
738
739 LogFlowFunc(("pCache=%#p uOffset=%llu pvBuf=%#p cbWrite=%zu pcbWritten=%#p\n",
740 pCache, uOffset, pcvBuf, cbWrite, pcbWritten));
741
742 AssertPtr(pCache);
743 AssertPtr(pcvBuf);
744 Assert(cbWrite > 0);
745
746 if (pcbWritten)
747 rc = pCache->Backend->pfnWrite(pCache->pBackendData, uOffset, pcvBuf,
748 cbWrite, pcbWritten);
749 else
750 {
751 size_t cbWritten = 0;
752
753 do
754 {
755 rc = pCache->Backend->pfnWrite(pCache->pBackendData, uOffset, pcvBuf,
756 cbWrite, &cbWritten);
757 uOffset += cbWritten;
758 pcvBuf = (char *)pcvBuf + cbWritten;
759 cbWrite -= cbWritten;
760 } while ( cbWrite
761 && RT_SUCCESS(rc));
762 }
763
764 LogFlowFunc(("returns rc=%Rrc pcbWritten=%zu\n",
765 rc, pcbWritten ? *pcbWritten : cbWrite));
766 return rc;
767}
768
769/**
770 * Internal: Reads a given amount of data from the image chain of the disk.
771 **/
772static int vdDiskReadHelper(PVBOXHDD pDisk, PVDIMAGE pImage, PVDIMAGE pImageParentOverride,
773 uint64_t uOffset, void *pvBuf, size_t cbRead, size_t *pcbThisRead)
774{
775 int rc = VINF_SUCCESS;
776 size_t cbThisRead = cbRead;
777
778 AssertPtr(pcbThisRead);
779
780 *pcbThisRead = 0;
781
782 /*
783 * Try to read from the given image.
784 * If the block is not allocated read from override chain if present.
785 */
786 rc = pImage->Backend->pfnRead(pImage->pBackendData,
787 uOffset, pvBuf, cbThisRead,
788 &cbThisRead);
789
790 if (rc == VERR_VD_BLOCK_FREE)
791 {
792 for (PVDIMAGE pCurrImage = pImageParentOverride ? pImageParentOverride : pImage->pPrev;
793 pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
794 pCurrImage = pCurrImage->pPrev)
795 {
796 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
797 uOffset, pvBuf, cbThisRead,
798 &cbThisRead);
799 }
800 }
801
802 if (RT_SUCCESS(rc) || rc == VERR_VD_BLOCK_FREE)
803 *pcbThisRead = cbThisRead;
804
805 return rc;
806}
807
808/**
809 * internal: read the specified amount of data in whatever blocks the backend
810 * will give us.
811 */
812static int vdReadHelper(PVBOXHDD pDisk, PVDIMAGE pImage, PVDIMAGE pImageParentOverride,
813 uint64_t uOffset, void *pvBuf, size_t cbRead,
814 bool fZeroFreeBlocks, bool fUpdateCache)
815{
816 int rc = VINF_SUCCESS;
817 size_t cbThisRead;
818 bool fAllFree = true;
819 size_t cbBufClear = 0;
820
821 /* Loop until all read. */
822 do
823 {
824 /* Search for image with allocated block. Do not attempt to read more
825 * than the previous reads marked as valid. Otherwise this would return
826 * stale data when different block sizes are used for the images. */
827 cbThisRead = cbRead;
828
829 if ( pDisk->pCache
830 && !pImageParentOverride)
831 {
832 rc = vdCacheReadHelper(pDisk->pCache, uOffset, pvBuf,
833 cbThisRead, &cbThisRead);
834
835 if (rc == VERR_VD_BLOCK_FREE)
836 {
837 rc = vdDiskReadHelper(pDisk, pImage, pImageParentOverride,
838 uOffset, pvBuf, cbThisRead, &cbThisRead);
839
840 /* If the read was successful, write the data back into the cache. */
841 if ( RT_SUCCESS(rc)
842 && fUpdateCache)
843 {
844 rc = vdCacheWriteHelper(pDisk->pCache, uOffset, pvBuf,
845 cbThisRead, NULL);
846 }
847 }
848 }
849 else
850 {
851 /** @todo can be be replaced by vdDiskReadHelper if it proves to be reliable,
852 * don't want to be responsible for data corruption...
853 */
854 /*
855 * Try to read from the given image.
856 * If the block is not allocated read from override chain if present.
857 */
858 rc = pImage->Backend->pfnRead(pImage->pBackendData,
859 uOffset, pvBuf, cbThisRead,
860 &cbThisRead);
861
862 if (rc == VERR_VD_BLOCK_FREE)
863 {
864 for (PVDIMAGE pCurrImage = pImageParentOverride ? pImageParentOverride : pImage->pPrev;
865 pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
866 pCurrImage = pCurrImage->pPrev)
867 {
868 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
869 uOffset, pvBuf, cbThisRead,
870 &cbThisRead);
871 }
872 }
873 }
874
875 /* No image in the chain contains the data for the block. */
876 if (rc == VERR_VD_BLOCK_FREE)
877 {
878 /* Fill the free space with 0 if we are told to do so
879 * or a previous read returned valid data. */
880 if (fZeroFreeBlocks || !fAllFree)
881 memset(pvBuf, '\0', cbThisRead);
882 else
883 cbBufClear += cbThisRead;
884
885 rc = VINF_SUCCESS;
886 }
887 else if (RT_SUCCESS(rc))
888 {
889 /* First not free block, fill the space before with 0. */
890 if (!fZeroFreeBlocks)
891 {
892 memset((char *)pvBuf - cbBufClear, '\0', cbBufClear);
893 cbBufClear = 0;
894 fAllFree = false;
895 }
896 }
897
898 cbRead -= cbThisRead;
899 uOffset += cbThisRead;
900 pvBuf = (char *)pvBuf + cbThisRead;
901 } while (cbRead != 0 && RT_SUCCESS(rc));
902
903 return (!fZeroFreeBlocks && fAllFree) ? VERR_VD_BLOCK_FREE : rc;
904}
905
906DECLINLINE(PVDIOCTX) vdIoCtxAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
907 uint64_t uOffset, size_t cbTransfer,
908 PVDIMAGE pImageStart,
909 PCRTSGBUF pcSgBuf, void *pvAllocation,
910 PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
911{
912 PVDIOCTX pIoCtx = NULL;
913
914 pIoCtx = (PVDIOCTX)RTMemCacheAlloc(pDisk->hMemCacheIoCtx);
915 if (RT_LIKELY(pIoCtx))
916 {
917 pIoCtx->pDisk = pDisk;
918 pIoCtx->enmTxDir = enmTxDir;
919 pIoCtx->cbTransferLeft = cbTransfer;
920 pIoCtx->uOffset = uOffset;
921 pIoCtx->cbTransfer = cbTransfer;
922 pIoCtx->pImageStart = pImageStart;
923 pIoCtx->pImageCur = pImageStart;
924 pIoCtx->cDataTransfersPending = 0;
925 pIoCtx->cMetaTransfersPending = 0;
926 pIoCtx->fComplete = false;
927 pIoCtx->fBlocked = false;
928 pIoCtx->pvAllocation = pvAllocation;
929 pIoCtx->pfnIoCtxTransfer = pfnIoCtxTransfer;
930 pIoCtx->pfnIoCtxTransferNext = NULL;
931 pIoCtx->rcReq = VINF_SUCCESS;
932
933 /* There is no S/G list for a flush request. */
934 if (enmTxDir != VDIOCTXTXDIR_FLUSH)
935 RTSgBufClone(&pIoCtx->SgBuf, pcSgBuf);
936 else
937 memset(&pIoCtx->SgBuf, 0, sizeof(RTSGBUF));
938 }
939
940 return pIoCtx;
941}
942
943DECLINLINE(PVDIOCTX) vdIoCtxRootAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
944 uint64_t uOffset, size_t cbTransfer,
945 PVDIMAGE pImageStart, PCRTSGBUF pcSgBuf,
946 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
947 void *pvUser1, void *pvUser2,
948 void *pvAllocation,
949 PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
950{
951 PVDIOCTX pIoCtx = vdIoCtxAlloc(pDisk, enmTxDir, uOffset, cbTransfer, pImageStart,
952 pcSgBuf, pvAllocation, pfnIoCtxTransfer);
953
954 if (RT_LIKELY(pIoCtx))
955 {
956 pIoCtx->pIoCtxParent = NULL;
957 pIoCtx->Type.Root.pfnComplete = pfnComplete;
958 pIoCtx->Type.Root.pvUser1 = pvUser1;
959 pIoCtx->Type.Root.pvUser2 = pvUser2;
960 }
961
962 LogFlow(("Allocated root I/O context %#p\n", pIoCtx));
963 return pIoCtx;
964}
965
966DECLINLINE(PVDIOCTX) vdIoCtxChildAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
967 uint64_t uOffset, size_t cbTransfer,
968 PVDIMAGE pImageStart, PCRTSGBUF pcSgBuf,
969 PVDIOCTX pIoCtxParent, size_t cbTransferParent,
970 size_t cbWriteParent, void *pvAllocation,
971 PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
972{
973 PVDIOCTX pIoCtx = vdIoCtxAlloc(pDisk, enmTxDir, uOffset, cbTransfer, pImageStart,
974 pcSgBuf, pvAllocation, pfnIoCtxTransfer);
975
976 AssertPtr(pIoCtxParent);
977 Assert(!pIoCtxParent->pIoCtxParent);
978
979 if (RT_LIKELY(pIoCtx))
980 {
981 pIoCtx->pIoCtxParent = pIoCtxParent;
982 pIoCtx->Type.Child.uOffsetSaved = uOffset;
983 pIoCtx->Type.Child.cbTransferLeftSaved = cbTransfer;
984 pIoCtx->Type.Child.cbTransferParent = cbTransferParent;
985 pIoCtx->Type.Child.cbWriteParent = cbWriteParent;
986 }
987
988 LogFlow(("Allocated child I/O context %#p\n", pIoCtx));
989 return pIoCtx;
990}
991
992DECLINLINE(PVDIOTASK) vdIoTaskUserAlloc(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnComplete, void *pvUser, PVDIOCTX pIoCtx, uint32_t cbTransfer)
993{
994 PVDIOTASK pIoTask = NULL;
995
996 pIoTask = (PVDIOTASK)RTMemCacheAlloc(pIoStorage->pVDIo->pDisk->hMemCacheIoTask);
997 if (pIoTask)
998 {
999 pIoTask->pIoStorage = pIoStorage;
1000 pIoTask->pfnComplete = pfnComplete;
1001 pIoTask->pvUser = pvUser;
1002 pIoTask->fMeta = false;
1003 pIoTask->Type.User.cbTransfer = cbTransfer;
1004 pIoTask->Type.User.pIoCtx = pIoCtx;
1005 }
1006
1007 return pIoTask;
1008}
1009
1010DECLINLINE(PVDIOTASK) vdIoTaskMetaAlloc(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnComplete, void *pvUser, PVDMETAXFER pMetaXfer)
1011{
1012 PVDIOTASK pIoTask = NULL;
1013
1014 pIoTask = (PVDIOTASK)RTMemCacheAlloc(pIoStorage->pVDIo->pDisk->hMemCacheIoTask);
1015 if (pIoTask)
1016 {
1017 pIoTask->pIoStorage = pIoStorage;
1018 pIoTask->pfnComplete = pfnComplete;
1019 pIoTask->pvUser = pvUser;
1020 pIoTask->fMeta = true;
1021 pIoTask->Type.Meta.pMetaXfer = pMetaXfer;
1022 }
1023
1024 return pIoTask;
1025}
1026
1027DECLINLINE(void) vdIoCtxFree(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
1028{
1029 LogFlow(("Freeing I/O context %#p\n", pIoCtx));
1030 if (pIoCtx->pvAllocation)
1031 RTMemFree(pIoCtx->pvAllocation);
1032#ifdef DEBUG
1033 memset(pIoCtx, 0xff, sizeof(VDIOCTX));
1034#endif
1035 RTMemCacheFree(pDisk->hMemCacheIoCtx, pIoCtx);
1036}
1037
1038DECLINLINE(void) vdIoTaskFree(PVBOXHDD pDisk, PVDIOTASK pIoTask)
1039{
1040 RTMemCacheFree(pDisk->hMemCacheIoTask, pIoTask);
1041}
1042
1043DECLINLINE(void) vdIoCtxChildReset(PVDIOCTX pIoCtx)
1044{
1045 AssertPtr(pIoCtx->pIoCtxParent);
1046
1047 RTSgBufReset(&pIoCtx->SgBuf);
1048 pIoCtx->uOffset = pIoCtx->Type.Child.uOffsetSaved;
1049 pIoCtx->cbTransferLeft = pIoCtx->Type.Child.cbTransferLeftSaved;
1050}
1051
1052DECLINLINE(PVDMETAXFER) vdMetaXferAlloc(PVDIOSTORAGE pIoStorage, uint64_t uOffset, size_t cb)
1053{
1054 PVDMETAXFER pMetaXfer = (PVDMETAXFER)RTMemAlloc(RT_OFFSETOF(VDMETAXFER, abData[cb]));
1055
1056 if (RT_LIKELY(pMetaXfer))
1057 {
1058 pMetaXfer->Core.Key = uOffset;
1059 pMetaXfer->Core.KeyLast = uOffset + cb - 1;
1060 pMetaXfer->fFlags = VDMETAXFER_TXDIR_NONE;
1061 pMetaXfer->cbMeta = cb;
1062 pMetaXfer->pIoStorage = pIoStorage;
1063 pMetaXfer->cRefs = 0;
1064 RTListInit(&pMetaXfer->ListIoCtxWaiting);
1065 }
1066 return pMetaXfer;
1067}
1068
1069DECLINLINE(int) vdIoCtxDefer(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
1070{
1071 PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
1072
1073 if (!pDeferred)
1074 return VERR_NO_MEMORY;
1075
1076 LogFlowFunc(("Deferring write pIoCtx=%#p\n", pIoCtx));
1077
1078 Assert(!pIoCtx->pIoCtxParent && !pIoCtx->fBlocked);
1079
1080 RTListInit(&pDeferred->NodeDeferred);
1081 pDeferred->pIoCtx = pIoCtx;
1082 RTListAppend(&pDisk->ListWriteLocked, &pDeferred->NodeDeferred);
1083 pIoCtx->fBlocked = true;
1084 return VINF_SUCCESS;
1085}
1086
1087static size_t vdIoCtxCopy(PVDIOCTX pIoCtxDst, PVDIOCTX pIoCtxSrc, size_t cbData)
1088{
1089 return RTSgBufCopy(&pIoCtxDst->SgBuf, &pIoCtxSrc->SgBuf, cbData);
1090}
1091
1092static int vdIoCtxCmp(PVDIOCTX pIoCtx1, PVDIOCTX pIoCtx2, size_t cbData)
1093{
1094 return RTSgBufCmp(&pIoCtx1->SgBuf, &pIoCtx2->SgBuf, cbData);
1095}
1096
1097static size_t vdIoCtxCopyTo(PVDIOCTX pIoCtx, uint8_t *pbData, size_t cbData)
1098{
1099 return RTSgBufCopyToBuf(&pIoCtx->SgBuf, pbData, cbData);
1100}
1101
1102
1103static size_t vdIoCtxCopyFrom(PVDIOCTX pIoCtx, uint8_t *pbData, size_t cbData)
1104{
1105 return RTSgBufCopyFromBuf(&pIoCtx->SgBuf, pbData, cbData);
1106}
1107
1108static size_t vdIoCtxSet(PVDIOCTX pIoCtx, uint8_t ch, size_t cbData)
1109{
1110 return RTSgBufSet(&pIoCtx->SgBuf, ch, cbData);
1111}
1112
1113static int vdIoCtxProcess(PVDIOCTX pIoCtx)
1114{
1115 int rc = VINF_SUCCESS;
1116 PVBOXHDD pDisk = pIoCtx->pDisk;
1117
1118 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
1119
1120 RTCritSectEnter(&pDisk->CritSect);
1121
1122 if ( !pIoCtx->cbTransferLeft
1123 && !pIoCtx->cMetaTransfersPending
1124 && !pIoCtx->cDataTransfersPending
1125 && !pIoCtx->pfnIoCtxTransfer)
1126 {
1127 rc = VINF_VD_ASYNC_IO_FINISHED;
1128 goto out;
1129 }
1130
1131 /*
1132 * We complete the I/O context in case of an error
1133 * if there is no I/O task pending.
1134 */
1135 if ( RT_FAILURE(pIoCtx->rcReq)
1136 && !pIoCtx->cMetaTransfersPending
1137 && !pIoCtx->cDataTransfersPending)
1138 {
1139 rc = VINF_VD_ASYNC_IO_FINISHED;
1140 goto out;
1141 }
1142
1143 /* Don't change anything if there is a metadata transfer pending or we are blocked. */
1144 if ( pIoCtx->cMetaTransfersPending
1145 || pIoCtx->fBlocked)
1146 {
1147 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1148 goto out;
1149 }
1150
1151 if (pIoCtx->pfnIoCtxTransfer)
1152 {
1153 /* Call the transfer function advancing to the next while there is no error. */
1154 while ( pIoCtx->pfnIoCtxTransfer
1155 && RT_SUCCESS(rc))
1156 {
1157 LogFlowFunc(("calling transfer function %#p\n", pIoCtx->pfnIoCtxTransfer));
1158 rc = pIoCtx->pfnIoCtxTransfer(pIoCtx);
1159
1160 /* Advance to the next part of the transfer if the current one succeeded. */
1161 if (RT_SUCCESS(rc))
1162 {
1163 pIoCtx->pfnIoCtxTransfer = pIoCtx->pfnIoCtxTransferNext;
1164 pIoCtx->pfnIoCtxTransferNext = NULL;
1165 }
1166 }
1167 }
1168
1169 if ( RT_SUCCESS(rc)
1170 && !pIoCtx->cbTransferLeft
1171 && !pIoCtx->cMetaTransfersPending
1172 && !pIoCtx->cDataTransfersPending)
1173 rc = VINF_VD_ASYNC_IO_FINISHED;
1174 else if ( RT_SUCCESS(rc)
1175 || rc == VERR_VD_NOT_ENOUGH_METADATA
1176 || rc == VERR_VD_IOCTX_HALT)
1177 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1178 else if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
1179 {
1180 ASMAtomicCmpXchgS32(&pIoCtx->rcReq, rc, VINF_SUCCESS);
1181 /*
1182 * The I/O context completed if we have an error and there is no data
1183 * or meta data transfer pending.
1184 */
1185 if ( !pIoCtx->cMetaTransfersPending
1186 && !pIoCtx->cDataTransfersPending)
1187 rc = VINF_VD_ASYNC_IO_FINISHED;
1188 else
1189 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1190 }
1191
1192out:
1193 RTCritSectLeave(&pDisk->CritSect);
1194
1195 LogFlowFunc(("pIoCtx=%#p rc=%Rrc cbTransferLeft=%u cMetaTransfersPending=%u fComplete=%RTbool\n",
1196 pIoCtx, rc, pIoCtx->cbTransferLeft, pIoCtx->cMetaTransfersPending,
1197 pIoCtx->fComplete));
1198
1199 return rc;
1200}
1201
1202static int vdIoCtxLockDisk(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
1203{
1204 int rc = VINF_SUCCESS;
1205
1206 if (!ASMAtomicCmpXchgBool(&pDisk->fLocked, true, false))
1207 {
1208 rc = vdIoCtxDefer(pDisk, pIoCtx);
1209 if (RT_SUCCESS(rc))
1210 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1211 }
1212
1213 return rc;
1214}
1215
1216static void vdIoCtxUnlockDisk(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
1217{
1218 Assert(pDisk->fLocked);
1219 ASMAtomicXchgBool(&pDisk->fLocked, false);
1220
1221 /* Process any pending writes if the current request didn't caused another growing. */
1222 RTCritSectEnter(&pDisk->CritSect);
1223
1224 if (!RTListIsEmpty(&pDisk->ListWriteLocked))
1225 {
1226 RTLISTNODE ListTmp;
1227
1228 RTListMove(&ListTmp, &pDisk->ListWriteLocked);
1229 RTCritSectLeave(&pDisk->CritSect);
1230
1231 /* Process the list. */
1232 do
1233 {
1234 int rc;
1235 PVDIOCTXDEFERRED pDeferred = RTListGetFirst(&ListTmp, VDIOCTXDEFERRED, NodeDeferred);
1236 PVDIOCTX pIoCtxWait = pDeferred->pIoCtx;
1237
1238 AssertPtr(pIoCtxWait);
1239
1240 RTListNodeRemove(&pDeferred->NodeDeferred);
1241 RTMemFree(pDeferred);
1242
1243 Assert(!pIoCtxWait->pIoCtxParent);
1244
1245 pIoCtxWait->fBlocked = false;
1246 LogFlowFunc(("Processing waiting I/O context pIoCtxWait=%#p\n", pIoCtxWait));
1247
1248 rc = vdIoCtxProcess(pIoCtxWait);
1249 if ( rc == VINF_VD_ASYNC_IO_FINISHED
1250 && ASMAtomicCmpXchgBool(&pIoCtxWait->fComplete, true, false))
1251 {
1252 LogFlowFunc(("Waiting I/O context completed pIoCtxWait=%#p\n", pIoCtxWait));
1253 vdThreadFinishWrite(pDisk);
1254 pIoCtxWait->Type.Root.pfnComplete(pIoCtxWait->Type.Root.pvUser1,
1255 pIoCtxWait->Type.Root.pvUser2,
1256 pIoCtxWait->rcReq);
1257 vdIoCtxFree(pDisk, pIoCtxWait);
1258 }
1259 } while (!RTListIsEmpty(&ListTmp));
1260 }
1261 else
1262 RTCritSectLeave(&pDisk->CritSect);
1263}
1264
1265/**
1266 * internal: read the specified amount of data in whatever blocks the backend
1267 * will give us - async version.
1268 */
1269static int vdReadHelperAsync(PVDIOCTX pIoCtx)
1270{
1271 int rc;
1272 size_t cbToRead = pIoCtx->cbTransfer;
1273 uint64_t uOffset = pIoCtx->uOffset;
1274 PVDIMAGE pCurrImage = NULL;
1275 size_t cbThisRead;
1276
1277 /* Loop until all reads started or we have a backend which needs to read metadata. */
1278 do
1279 {
1280 pCurrImage = pIoCtx->pImageCur;
1281
1282 /* Search for image with allocated block. Do not attempt to read more
1283 * than the previous reads marked as valid. Otherwise this would return
1284 * stale data when different block sizes are used for the images. */
1285 cbThisRead = cbToRead;
1286
1287 /*
1288 * Try to read from the given image.
1289 * If the block is not allocated read from override chain if present.
1290 */
1291 rc = pCurrImage->Backend->pfnAsyncRead(pCurrImage->pBackendData,
1292 uOffset, cbThisRead,
1293 pIoCtx, &cbThisRead);
1294
1295 if (rc == VERR_VD_BLOCK_FREE)
1296 {
1297 for (pCurrImage = pCurrImage->pPrev;
1298 pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
1299 pCurrImage = pCurrImage->pPrev)
1300 {
1301 rc = pCurrImage->Backend->pfnAsyncRead(pCurrImage->pBackendData,
1302 uOffset, cbThisRead,
1303 pIoCtx, &cbThisRead);
1304 }
1305 }
1306
1307 /* The task state will be updated on success already, don't do it here!. */
1308 if (rc == VERR_VD_BLOCK_FREE)
1309 {
1310 /* No image in the chain contains the data for the block. */
1311 vdIoCtxSet(pIoCtx, '\0', cbThisRead);
1312 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbThisRead);
1313 rc = VINF_SUCCESS;
1314 }
1315 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1316 rc = VINF_SUCCESS;
1317 else if (rc == VERR_VD_IOCTX_HALT)
1318 {
1319 uOffset += cbThisRead;
1320 cbToRead -= cbThisRead;
1321 pIoCtx->fBlocked = true;
1322 }
1323
1324 if (RT_FAILURE(rc))
1325 break;
1326
1327 cbToRead -= cbThisRead;
1328 uOffset += cbThisRead;
1329 } while (cbToRead != 0 && RT_SUCCESS(rc));
1330
1331 if ( rc == VERR_VD_NOT_ENOUGH_METADATA
1332 || rc == VERR_VD_IOCTX_HALT)
1333 {
1334 /* Save the current state. */
1335 pIoCtx->uOffset = uOffset;
1336 pIoCtx->cbTransfer = cbToRead;
1337 pIoCtx->pImageCur = pCurrImage ? pCurrImage : pIoCtx->pImageStart;
1338 }
1339
1340 return rc;
1341}
1342
1343/**
1344 * internal: parent image read wrapper for compacting.
1345 */
1346static int vdParentRead(void *pvUser, uint64_t uOffset, void *pvBuf,
1347 size_t cbRead)
1348{
1349 PVDPARENTSTATEDESC pParentState = (PVDPARENTSTATEDESC)pvUser;
1350 return vdReadHelper(pParentState->pDisk, pParentState->pImage, NULL, uOffset,
1351 pvBuf, cbRead, true /* fZeroFreeBlocks */,
1352 false /* fUpdateCache */);
1353}
1354
1355/**
1356 * internal: mark the disk as not modified.
1357 */
1358static void vdResetModifiedFlag(PVBOXHDD pDisk)
1359{
1360 if (pDisk->uModified & VD_IMAGE_MODIFIED_FLAG)
1361 {
1362 /* generate new last-modified uuid */
1363 if (!(pDisk->uModified & VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
1364 {
1365 RTUUID Uuid;
1366
1367 RTUuidCreate(&Uuid);
1368 pDisk->pLast->Backend->pfnSetModificationUuid(pDisk->pLast->pBackendData,
1369 &Uuid);
1370
1371 if (pDisk->pCache)
1372 pDisk->pCache->Backend->pfnSetModificationUuid(pDisk->pCache->pBackendData,
1373 &Uuid);
1374 }
1375
1376 pDisk->uModified &= ~VD_IMAGE_MODIFIED_FLAG;
1377 }
1378}
1379
1380/**
1381 * internal: mark the disk as modified.
1382 */
1383static void vdSetModifiedFlag(PVBOXHDD pDisk)
1384{
1385 pDisk->uModified |= VD_IMAGE_MODIFIED_FLAG;
1386 if (pDisk->uModified & VD_IMAGE_MODIFIED_FIRST)
1387 {
1388 pDisk->uModified &= ~VD_IMAGE_MODIFIED_FIRST;
1389
1390 /* First modify, so create a UUID and ensure it's written to disk. */
1391 vdResetModifiedFlag(pDisk);
1392
1393 if (!(pDisk->uModified & VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
1394 pDisk->pLast->Backend->pfnFlush(pDisk->pLast->pBackendData);
1395 }
1396}
1397
1398/**
1399 * internal: write a complete block (only used for diff images), taking the
1400 * remaining data from parent images. This implementation does not optimize
1401 * anything (except that it tries to read only that portions from parent
1402 * images that are really needed).
1403 */
1404static int vdWriteHelperStandard(PVBOXHDD pDisk, PVDIMAGE pImage,
1405 PVDIMAGE pImageParentOverride,
1406 uint64_t uOffset, size_t cbWrite,
1407 size_t cbThisWrite, size_t cbPreRead,
1408 size_t cbPostRead, const void *pvBuf,
1409 void *pvTmp)
1410{
1411 int rc = VINF_SUCCESS;
1412
1413 /* Read the data that goes before the write to fill the block. */
1414 if (cbPreRead)
1415 {
1416 /*
1417 * Updating the cache doesn't make sense here because
1418 * this will be done after the complete block was written.
1419 */
1420 rc = vdReadHelper(pDisk, pImage, pImageParentOverride,
1421 uOffset - cbPreRead, pvTmp, cbPreRead,
1422 true /* fZeroFreeBlocks*/,
1423 false /* fUpdateCache */);
1424 if (RT_FAILURE(rc))
1425 return rc;
1426 }
1427
1428 /* Copy the data to the right place in the buffer. */
1429 memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
1430
1431 /* Read the data that goes after the write to fill the block. */
1432 if (cbPostRead)
1433 {
1434 /* If we have data to be written, use that instead of reading
1435 * data from the image. */
1436 size_t cbWriteCopy;
1437 if (cbWrite > cbThisWrite)
1438 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
1439 else
1440 cbWriteCopy = 0;
1441 /* Figure out how much we cannot read from the image, because
1442 * the last block to write might exceed the nominal size of the
1443 * image for technical reasons. */
1444 size_t cbFill;
1445 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
1446 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
1447 else
1448 cbFill = 0;
1449 /* The rest must be read from the image. */
1450 size_t cbReadImage = cbPostRead - cbWriteCopy - cbFill;
1451
1452 /* Now assemble the remaining data. */
1453 if (cbWriteCopy)
1454 memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
1455 (char *)pvBuf + cbThisWrite, cbWriteCopy);
1456 if (cbReadImage)
1457 rc = vdReadHelper(pDisk, pImage, pImageParentOverride,
1458 uOffset + cbThisWrite + cbWriteCopy,
1459 (char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy,
1460 cbReadImage, true /* fZeroFreeBlocks */,
1461 false /* fUpdateCache */);
1462 if (RT_FAILURE(rc))
1463 return rc;
1464 /* Zero out the remainder of this block. Will never be visible, as this
1465 * is beyond the limit of the image. */
1466 if (cbFill)
1467 memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
1468 '\0', cbFill);
1469 }
1470
1471 /* Write the full block to the virtual disk. */
1472 rc = pImage->Backend->pfnWrite(pImage->pBackendData,
1473 uOffset - cbPreRead, pvTmp,
1474 cbPreRead + cbThisWrite + cbPostRead,
1475 NULL, &cbPreRead, &cbPostRead, 0);
1476 Assert(rc != VERR_VD_BLOCK_FREE);
1477 Assert(cbPreRead == 0);
1478 Assert(cbPostRead == 0);
1479
1480 return rc;
1481}
1482
1483/**
1484 * internal: write a complete block (only used for diff images), taking the
1485 * remaining data from parent images. This implementation optimizes out writes
1486 * that do not change the data relative to the state as of the parent images.
1487 * All backends which support differential/growing images support this.
1488 */
1489static int vdWriteHelperOptimized(PVBOXHDD pDisk, PVDIMAGE pImage,
1490 PVDIMAGE pImageParentOverride,
1491 uint64_t uOffset, size_t cbWrite,
1492 size_t cbThisWrite, size_t cbPreRead,
1493 size_t cbPostRead, const void *pvBuf,
1494 void *pvTmp)
1495{
1496 size_t cbFill = 0;
1497 size_t cbWriteCopy = 0;
1498 size_t cbReadImage = 0;
1499 int rc;
1500
1501 if (cbPostRead)
1502 {
1503 /* Figure out how much we cannot read from the image, because
1504 * the last block to write might exceed the nominal size of the
1505 * image for technical reasons. */
1506 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
1507 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
1508
1509 /* If we have data to be written, use that instead of reading
1510 * data from the image. */
1511 if (cbWrite > cbThisWrite)
1512 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
1513
1514 /* The rest must be read from the image. */
1515 cbReadImage = cbPostRead - cbWriteCopy - cbFill;
1516 }
1517
1518 /* Read the entire data of the block so that we can compare whether it will
1519 * be modified by the write or not. */
1520 rc = vdReadHelper(pDisk, pImage, pImageParentOverride, uOffset - cbPreRead, pvTmp,
1521 cbPreRead + cbThisWrite + cbPostRead - cbFill,
1522 true /* fZeroFreeBlocks */,
1523 false /* fUpdateCache */);
1524 if (RT_FAILURE(rc))
1525 return rc;
1526
1527 /* Check if the write would modify anything in this block. */
1528 if ( !memcmp((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite)
1529 && (!cbWriteCopy || !memcmp((char *)pvTmp + cbPreRead + cbThisWrite,
1530 (char *)pvBuf + cbThisWrite, cbWriteCopy)))
1531 {
1532 /* Block is completely unchanged, so no need to write anything. */
1533 return VINF_SUCCESS;
1534 }
1535
1536 /* Copy the data to the right place in the buffer. */
1537 memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
1538
1539 /* Handle the data that goes after the write to fill the block. */
1540 if (cbPostRead)
1541 {
1542 /* Now assemble the remaining data. */
1543 if (cbWriteCopy)
1544 memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
1545 (char *)pvBuf + cbThisWrite, cbWriteCopy);
1546 /* Zero out the remainder of this block. Will never be visible, as this
1547 * is beyond the limit of the image. */
1548 if (cbFill)
1549 memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
1550 '\0', cbFill);
1551 }
1552
1553 /* Write the full block to the virtual disk. */
1554 rc = pImage->Backend->pfnWrite(pImage->pBackendData,
1555 uOffset - cbPreRead, pvTmp,
1556 cbPreRead + cbThisWrite + cbPostRead,
1557 NULL, &cbPreRead, &cbPostRead, 0);
1558 Assert(rc != VERR_VD_BLOCK_FREE);
1559 Assert(cbPreRead == 0);
1560 Assert(cbPostRead == 0);
1561
1562 return rc;
1563}
1564
1565/**
1566 * internal: write buffer to the image, taking care of block boundaries and
1567 * write optimizations.
1568 */
1569static int vdWriteHelper(PVBOXHDD pDisk, PVDIMAGE pImage,
1570 PVDIMAGE pImageParentOverride, uint64_t uOffset,
1571 const void *pvBuf, size_t cbWrite,
1572 bool fUpdateCache)
1573{
1574 int rc;
1575 unsigned fWrite;
1576 size_t cbThisWrite;
1577 size_t cbPreRead, cbPostRead;
1578 uint64_t uOffsetCur = uOffset;
1579 size_t cbWriteCur = cbWrite;
1580 const void *pcvBufCur = pvBuf;
1581
1582 /* Loop until all written. */
1583 do
1584 {
1585 /* Try to write the possibly partial block to the last opened image.
1586 * This works when the block is already allocated in this image or
1587 * if it is a full-block write (and allocation isn't suppressed below).
1588 * For image formats which don't support zero blocks, it's beneficial
1589 * to avoid unnecessarily allocating unchanged blocks. This prevents
1590 * unwanted expanding of images. VMDK is an example. */
1591 cbThisWrite = cbWriteCur;
1592 fWrite = (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
1593 ? 0 : VD_WRITE_NO_ALLOC;
1594 rc = pImage->Backend->pfnWrite(pImage->pBackendData, uOffsetCur, pcvBufCur,
1595 cbThisWrite, &cbThisWrite, &cbPreRead,
1596 &cbPostRead, fWrite);
1597 if (rc == VERR_VD_BLOCK_FREE)
1598 {
1599 void *pvTmp = RTMemTmpAlloc(cbPreRead + cbThisWrite + cbPostRead);
1600 AssertBreakStmt(VALID_PTR(pvTmp), rc = VERR_NO_MEMORY);
1601
1602 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME))
1603 {
1604 /* Optimized write, suppress writing to a so far unallocated
1605 * block if the data is in fact not changed. */
1606 rc = vdWriteHelperOptimized(pDisk, pImage, pImageParentOverride,
1607 uOffsetCur, cbWriteCur,
1608 cbThisWrite, cbPreRead, cbPostRead,
1609 pcvBufCur, pvTmp);
1610 }
1611 else
1612 {
1613 /* Normal write, not optimized in any way. The block will
1614 * be written no matter what. This will usually (unless the
1615 * backend has some further optimization enabled) cause the
1616 * block to be allocated. */
1617 rc = vdWriteHelperStandard(pDisk, pImage, pImageParentOverride,
1618 uOffsetCur, cbWriteCur,
1619 cbThisWrite, cbPreRead, cbPostRead,
1620 pcvBufCur, pvTmp);
1621 }
1622 RTMemTmpFree(pvTmp);
1623 if (RT_FAILURE(rc))
1624 break;
1625 }
1626
1627 cbWriteCur -= cbThisWrite;
1628 uOffsetCur += cbThisWrite;
1629 pcvBufCur = (char *)pcvBufCur + cbThisWrite;
1630 } while (cbWriteCur != 0 && RT_SUCCESS(rc));
1631
1632 /* Update the cache on success */
1633 if ( RT_SUCCESS(rc)
1634 && pDisk->pCache
1635 && fUpdateCache)
1636 rc = vdCacheWriteHelper(pDisk->pCache, uOffset, pvBuf, cbWrite, NULL);
1637
1638 return rc;
1639}
1640
1641/**
1642 * internal: write a complete block (only used for diff images), taking the
1643 * remaining data from parent images. This implementation does not optimize
1644 * anything (except that it tries to read only that portions from parent
1645 * images that are really needed) - async version.
1646 */
1647static int vdWriteHelperStandardAsync(PVDIOCTX pIoCtx)
1648{
1649 int rc = VINF_SUCCESS;
1650
1651#if 0
1652
1653 /* Read the data that goes before the write to fill the block. */
1654 if (cbPreRead)
1655 {
1656 rc = vdReadHelperAsync(pIoCtxDst);
1657 if (RT_FAILURE(rc))
1658 return rc;
1659 }
1660
1661 /* Copy the data to the right place in the buffer. */
1662 vdIoCtxCopy(pIoCtxDst, pIoCtxSrc, cbThisWrite);
1663
1664 /* Read the data that goes after the write to fill the block. */
1665 if (cbPostRead)
1666 {
1667 /* If we have data to be written, use that instead of reading
1668 * data from the image. */
1669 size_t cbWriteCopy;
1670 if (cbWrite > cbThisWrite)
1671 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
1672 else
1673 cbWriteCopy = 0;
1674 /* Figure out how much we cannot read from the image, because
1675 * the last block to write might exceed the nominal size of the
1676 * image for technical reasons. */
1677 size_t cbFill;
1678 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
1679 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
1680 else
1681 cbFill = 0;
1682 /* The rest must be read from the image. */
1683 size_t cbReadImage = cbPostRead - cbWriteCopy - cbFill;
1684
1685 /* Now assemble the remaining data. */
1686 if (cbWriteCopy)
1687 {
1688 vdIoCtxCopy(pIoCtxDst, pIoCtxSrc, cbWriteCopy);
1689 ASMAtomicSubU32(&pIoCtxDst->cbTransferLeft, cbWriteCopy);
1690 }
1691
1692 if (cbReadImage)
1693 rc = vdReadHelperAsync(pDisk, pImage, pImageParentOverride, pIoCtxDst,
1694 uOffset + cbThisWrite + cbWriteCopy,
1695 cbReadImage);
1696 if (RT_FAILURE(rc))
1697 return rc;
1698 /* Zero out the remainder of this block. Will never be visible, as this
1699 * is beyond the limit of the image. */
1700 if (cbFill)
1701 {
1702 vdIoCtxSet(pIoCtxDst, '\0', cbFill);
1703 ASMAtomicSubU32(&pIoCtxDst->cbTransferLeft, cbFill);
1704 }
1705 }
1706
1707 if ( !pIoCtxDst->cbTransferLeft
1708 && !pIoCtxDst->cMetaTransfersPending
1709 && ASMAtomicCmpXchgBool(&pIoCtxDst->fComplete, true, false))
1710 {
1711 /* Write the full block to the virtual disk. */
1712 vdIoCtxChildReset(pIoCtxDst);
1713 rc = pImage->Backend->pfnAsyncWrite(pImage->pBackendData,
1714 uOffset - cbPreRead,
1715 cbPreRead + cbThisWrite + cbPostRead,
1716 pIoCtxDst,
1717 NULL, &cbPreRead, &cbPostRead, 0);
1718 Assert(rc != VERR_VD_BLOCK_FREE);
1719 Assert(cbPreRead == 0);
1720 Assert(cbPostRead == 0);
1721 }
1722 else
1723 {
1724 LogFlow(("cbTransferLeft=%u cMetaTransfersPending=%u fComplete=%RTbool\n",
1725 pIoCtxDst->cbTransferLeft, pIoCtxDst->cMetaTransfersPending,
1726 pIoCtxDst->fComplete));
1727 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1728 }
1729
1730 return rc;
1731#endif
1732 return VERR_NOT_IMPLEMENTED;
1733}
1734
1735static int vdWriteHelperOptimizedCommitAsync(PVDIOCTX pIoCtx)
1736{
1737 int rc = VINF_SUCCESS;
1738 PVDIMAGE pImage = pIoCtx->pImageStart;
1739 size_t cbPreRead = pIoCtx->Type.Child.cbPreRead;
1740 size_t cbPostRead = pIoCtx->Type.Child.cbPostRead;
1741 size_t cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
1742
1743 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
1744 rc = pImage->Backend->pfnAsyncWrite(pImage->pBackendData,
1745 pIoCtx->uOffset - cbPreRead,
1746 cbPreRead + cbThisWrite + cbPostRead,
1747 pIoCtx, NULL, &cbPreRead, &cbPostRead, 0);
1748 Assert(rc != VERR_VD_BLOCK_FREE);
1749 Assert(rc == VERR_VD_NOT_ENOUGH_METADATA || cbPreRead == 0);
1750 Assert(rc == VERR_VD_NOT_ENOUGH_METADATA || cbPostRead == 0);
1751 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1752 rc = VINF_SUCCESS;
1753 else if (rc == VERR_VD_IOCTX_HALT)
1754 {
1755 pIoCtx->fBlocked = true;
1756 rc = VINF_SUCCESS;
1757 }
1758
1759 LogFlowFunc(("returns rc=%Rrc\n", rc));
1760 return rc;
1761}
1762
1763static int vdWriteHelperOptimizedCmpAndWriteAsync(PVDIOCTX pIoCtx)
1764{
1765 int rc = VINF_SUCCESS;
1766 PVDIMAGE pImage = pIoCtx->pImageCur;
1767 size_t cbThisWrite = 0;
1768 size_t cbPreRead = pIoCtx->Type.Child.cbPreRead;
1769 size_t cbPostRead = pIoCtx->Type.Child.cbPostRead;
1770 size_t cbWriteCopy = pIoCtx->Type.Child.Write.Optimized.cbWriteCopy;
1771 size_t cbFill = pIoCtx->Type.Child.Write.Optimized.cbFill;
1772 size_t cbReadImage = pIoCtx->Type.Child.Write.Optimized.cbReadImage;
1773 PVDIOCTX pIoCtxParent = pIoCtx->pIoCtxParent;
1774
1775 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
1776
1777 AssertPtr(pIoCtxParent);
1778 Assert(!pIoCtxParent->pIoCtxParent);
1779 Assert(!pIoCtx->cbTransferLeft && !pIoCtx->cMetaTransfersPending);
1780
1781 vdIoCtxChildReset(pIoCtx);
1782 cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
1783 RTSgBufAdvance(&pIoCtx->SgBuf, cbPreRead);
1784
1785 /* Check if the write would modify anything in this block. */
1786 if (!RTSgBufCmp(&pIoCtx->SgBuf, &pIoCtxParent->SgBuf, cbThisWrite))
1787 {
1788 RTSGBUF SgBufSrcTmp;
1789
1790 RTSgBufClone(&SgBufSrcTmp, &pIoCtxParent->SgBuf);
1791 RTSgBufAdvance(&SgBufSrcTmp, cbThisWrite);
1792 RTSgBufAdvance(&pIoCtx->SgBuf, cbThisWrite);
1793
1794 if (!cbWriteCopy || !RTSgBufCmp(&pIoCtx->SgBuf, &SgBufSrcTmp, cbWriteCopy))
1795 {
1796 /* Block is completely unchanged, so no need to write anything. */
1797 LogFlowFunc(("Block didn't changed\n"));
1798 ASMAtomicWriteU32(&pIoCtx->cbTransferLeft, 0);
1799 RTSgBufAdvance(&pIoCtxParent->SgBuf, cbThisWrite);
1800 return VINF_VD_ASYNC_IO_FINISHED;
1801 }
1802 }
1803
1804 /* Copy the data to the right place in the buffer. */
1805 RTSgBufReset(&pIoCtx->SgBuf);
1806 RTSgBufAdvance(&pIoCtx->SgBuf, cbPreRead);
1807 vdIoCtxCopy(pIoCtx, pIoCtxParent, cbThisWrite);
1808
1809 /* Handle the data that goes after the write to fill the block. */
1810 if (cbPostRead)
1811 {
1812 /* Now assemble the remaining data. */
1813 if (cbWriteCopy)
1814 {
1815 /*
1816 * The S/G buffer of the parent needs to be cloned because
1817 * it is not allowed to modify the state.
1818 */
1819 RTSGBUF SgBufParentTmp;
1820
1821 RTSgBufClone(&SgBufParentTmp, &pIoCtxParent->SgBuf);
1822 RTSgBufCopy(&pIoCtx->SgBuf, &SgBufParentTmp, cbWriteCopy);
1823 }
1824
1825 /* Zero out the remainder of this block. Will never be visible, as this
1826 * is beyond the limit of the image. */
1827 if (cbFill)
1828 {
1829 RTSgBufAdvance(&pIoCtx->SgBuf, cbReadImage);
1830 vdIoCtxSet(pIoCtx, '\0', cbFill);
1831 }
1832 }
1833
1834 /* Write the full block to the virtual disk. */
1835 RTSgBufReset(&pIoCtx->SgBuf);
1836 pIoCtx->pfnIoCtxTransferNext = vdWriteHelperOptimizedCommitAsync;
1837
1838 return rc;
1839}
1840
1841static int vdWriteHelperOptimizedPreReadAsync(PVDIOCTX pIoCtx)
1842{
1843 int rc = VINF_SUCCESS;
1844
1845 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
1846
1847 if (pIoCtx->cbTransferLeft)
1848 rc = vdReadHelperAsync(pIoCtx);
1849
1850 if ( RT_SUCCESS(rc)
1851 && ( pIoCtx->cbTransferLeft
1852 || pIoCtx->cMetaTransfersPending))
1853 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1854 else
1855 pIoCtx->pfnIoCtxTransferNext = vdWriteHelperOptimizedCmpAndWriteAsync;
1856
1857 return rc;
1858}
1859
1860/**
1861 * internal: write a complete block (only used for diff images), taking the
1862 * remaining data from parent images. This implementation optimizes out writes
1863 * that do not change the data relative to the state as of the parent images.
1864 * All backends which support differential/growing images support this - async version.
1865 */
1866static int vdWriteHelperOptimizedAsync(PVDIOCTX pIoCtx)
1867{
1868 PVBOXHDD pDisk = pIoCtx->pDisk;
1869 uint64_t uOffset = pIoCtx->Type.Child.uOffsetSaved;
1870 size_t cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
1871 size_t cbPreRead = pIoCtx->Type.Child.cbPreRead;
1872 size_t cbPostRead = pIoCtx->Type.Child.cbPostRead;
1873 size_t cbWrite = pIoCtx->Type.Child.cbWriteParent;
1874 size_t cbFill = 0;
1875 size_t cbWriteCopy = 0;
1876 size_t cbReadImage = 0;
1877
1878 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
1879
1880 AssertPtr(pIoCtx->pIoCtxParent);
1881 Assert(!pIoCtx->pIoCtxParent->pIoCtxParent);
1882
1883 if (cbPostRead)
1884 {
1885 /* Figure out how much we cannot read from the image, because
1886 * the last block to write might exceed the nominal size of the
1887 * image for technical reasons. */
1888 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
1889 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
1890
1891 /* If we have data to be written, use that instead of reading
1892 * data from the image. */
1893 if (cbWrite > cbThisWrite)
1894 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
1895
1896 /* The rest must be read from the image. */
1897 cbReadImage = cbPostRead - cbWriteCopy - cbFill;
1898 }
1899
1900 pIoCtx->Type.Child.Write.Optimized.cbFill = cbFill;
1901 pIoCtx->Type.Child.Write.Optimized.cbWriteCopy = cbWriteCopy;
1902 pIoCtx->Type.Child.Write.Optimized.cbReadImage = cbReadImage;
1903
1904 /* Read the entire data of the block so that we can compare whether it will
1905 * be modified by the write or not. */
1906 pIoCtx->cbTransferLeft = cbPreRead + cbThisWrite + cbPostRead - cbFill;
1907 pIoCtx->cbTransfer = pIoCtx->cbTransferLeft;
1908 pIoCtx->uOffset -= cbPreRead;
1909
1910 /* Next step */
1911 pIoCtx->pfnIoCtxTransferNext = vdWriteHelperOptimizedPreReadAsync;
1912 return VINF_SUCCESS;
1913}
1914
1915/**
1916 * internal: write buffer to the image, taking care of block boundaries and
1917 * write optimizations - async version.
1918 */
1919static int vdWriteHelperAsync(PVDIOCTX pIoCtx)
1920{
1921 int rc;
1922 size_t cbWrite = pIoCtx->cbTransfer;
1923 uint64_t uOffset = pIoCtx->uOffset;
1924 PVDIMAGE pImage = pIoCtx->pImageCur;
1925 PVBOXHDD pDisk = pIoCtx->pDisk;
1926 unsigned fWrite;
1927 size_t cbThisWrite;
1928 size_t cbPreRead, cbPostRead;
1929
1930 /* Loop until all written. */
1931 do
1932 {
1933 /* Try to write the possibly partial block to the last opened image.
1934 * This works when the block is already allocated in this image or
1935 * if it is a full-block write (and allocation isn't suppressed below).
1936 * For image formats which don't support zero blocks, it's beneficial
1937 * to avoid unnecessarily allocating unchanged blocks. This prevents
1938 * unwanted expanding of images. VMDK is an example. */
1939 cbThisWrite = cbWrite;
1940 fWrite = (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
1941 ? 0 : VD_WRITE_NO_ALLOC;
1942 rc = pImage->Backend->pfnAsyncWrite(pImage->pBackendData, uOffset,
1943 cbThisWrite, pIoCtx,
1944 &cbThisWrite, &cbPreRead,
1945 &cbPostRead, fWrite);
1946 if (rc == VERR_VD_BLOCK_FREE)
1947 {
1948 /* Lock the disk .*/
1949 rc = vdIoCtxLockDisk(pDisk, pIoCtx);
1950 if (RT_SUCCESS(rc))
1951 {
1952 /*
1953 * Allocate segment and buffer in one go.
1954 * A bit hackish but avoids the need to allocate memory twice.
1955 */
1956 PRTSGBUF pTmp = (PRTSGBUF)RTMemAlloc(cbPreRead + cbThisWrite + cbPostRead + sizeof(RTSGSEG) + sizeof(RTSGBUF));
1957 AssertBreakStmt(VALID_PTR(pTmp), rc = VERR_NO_MEMORY);
1958 PRTSGSEG pSeg = (PRTSGSEG)(pTmp + 1);
1959
1960 pSeg->pvSeg = pSeg + 1;
1961 pSeg->cbSeg = cbPreRead + cbThisWrite + cbPostRead;
1962 RTSgBufInit(pTmp, pSeg, 1);
1963
1964 PVDIOCTX pIoCtxWrite = vdIoCtxChildAlloc(pDisk, VDIOCTXTXDIR_WRITE,
1965 uOffset, pSeg->cbSeg, pImage,
1966 pTmp,
1967 pIoCtx, cbThisWrite,
1968 cbWrite,
1969 pTmp,
1970 (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
1971 ? vdWriteHelperStandardAsync
1972 : vdWriteHelperOptimizedAsync);
1973 if (!VALID_PTR(pIoCtxWrite))
1974 {
1975 RTMemTmpFree(pTmp);
1976 rc = VERR_NO_MEMORY;
1977 break;
1978 }
1979
1980 LogFlowFunc(("Disk is growing because of pIoCtx=%#p pIoCtxWrite=%#p\n",
1981 pIoCtx, pIoCtxWrite));
1982
1983 pIoCtxWrite->Type.Child.cbPreRead = cbPreRead;
1984 pIoCtxWrite->Type.Child.cbPostRead = cbPostRead;
1985
1986 /* Process the write request */
1987 rc = vdIoCtxProcess(pIoCtxWrite);
1988
1989 if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
1990 {
1991 vdIoCtxFree(pDisk, pIoCtxWrite);
1992 break;
1993 }
1994 else if ( rc == VINF_VD_ASYNC_IO_FINISHED
1995 && ASMAtomicCmpXchgBool(&pIoCtxWrite->fComplete, true, false))
1996 {
1997 LogFlow(("Child write request completed\n"));
1998 Assert(pIoCtx->cbTransferLeft >= cbThisWrite);
1999 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbThisWrite);
2000 ASMAtomicWriteBool(&pDisk->fLocked, false);
2001 vdIoCtxFree(pDisk, pIoCtxWrite);
2002
2003 rc = VINF_SUCCESS;
2004 }
2005 else
2006 {
2007 LogFlow(("Child write pending\n"));
2008 pIoCtx->fBlocked = true;
2009 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2010 cbWrite -= cbThisWrite;
2011 uOffset += cbThisWrite;
2012 break;
2013 }
2014 }
2015 else
2016 {
2017 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2018 break;
2019 }
2020 }
2021
2022 if (rc == VERR_VD_IOCTX_HALT)
2023 {
2024 cbWrite -= cbThisWrite;
2025 uOffset += cbThisWrite;
2026 pIoCtx->fBlocked = true;
2027 break;
2028 }
2029 else if (rc == VERR_VD_NOT_ENOUGH_METADATA)
2030 break;
2031
2032 cbWrite -= cbThisWrite;
2033 uOffset += cbThisWrite;
2034 } while (cbWrite != 0 && (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS));
2035
2036 if ( rc == VERR_VD_ASYNC_IO_IN_PROGRESS
2037 || rc == VERR_VD_NOT_ENOUGH_METADATA
2038 || rc == VERR_VD_IOCTX_HALT)
2039 {
2040 /*
2041 * Tell the caller that we don't need to go back here because all
2042 * writes are initiated.
2043 */
2044 if (!cbWrite)
2045 rc = VINF_SUCCESS;
2046
2047 pIoCtx->uOffset = uOffset;
2048 pIoCtx->cbTransfer = cbWrite;
2049 }
2050
2051 return rc;
2052}
2053
2054/**
2055 * Flush helper async version.
2056 */
2057static int vdFlushHelperAsync(PVDIOCTX pIoCtx)
2058{
2059 int rc = VINF_SUCCESS;
2060 PVBOXHDD pDisk = pIoCtx->pDisk;
2061 PVDIMAGE pImage = pIoCtx->pImageCur;
2062
2063 rc = vdIoCtxLockDisk(pDisk, pIoCtx);
2064 if (RT_SUCCESS(rc))
2065 {
2066 vdResetModifiedFlag(pDisk);
2067 rc = pImage->Backend->pfnAsyncFlush(pImage->pBackendData, pIoCtx);
2068 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2069 rc = VINF_SUCCESS;
2070 }
2071
2072 return rc;
2073}
2074
2075/**
2076 * internal: scans plugin directory and loads the backends have been found.
2077 */
2078static int vdLoadDynamicBackends()
2079{
2080#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
2081 int rc = VINF_SUCCESS;
2082 PRTDIR pPluginDir = NULL;
2083
2084 /* Enumerate plugin backends. */
2085 char szPath[RTPATH_MAX];
2086 rc = RTPathAppPrivateArch(szPath, sizeof(szPath));
2087 if (RT_FAILURE(rc))
2088 return rc;
2089
2090 /* To get all entries with VBoxHDD as prefix. */
2091 char *pszPluginFilter = RTPathJoinA(szPath, VBOX_HDDFORMAT_PLUGIN_PREFIX "*");
2092 if (!pszPluginFilter)
2093 return VERR_NO_STR_MEMORY;
2094
2095 PRTDIRENTRYEX pPluginDirEntry = NULL;
2096 size_t cbPluginDirEntry = sizeof(RTDIRENTRYEX);
2097 /* The plugins are in the same directory as the other shared libs. */
2098 rc = RTDirOpenFiltered(&pPluginDir, pszPluginFilter, RTDIRFILTER_WINNT);
2099 if (RT_FAILURE(rc))
2100 {
2101 /* On Windows the above immediately signals that there are no
2102 * files matching, while on other platforms enumerating the
2103 * files below fails. Either way: no plugins. */
2104 goto out;
2105 }
2106
2107 pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
2108 if (!pPluginDirEntry)
2109 {
2110 rc = VERR_NO_MEMORY;
2111 goto out;
2112 }
2113
2114 while ((rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK)) != VERR_NO_MORE_FILES)
2115 {
2116 RTLDRMOD hPlugin = NIL_RTLDRMOD;
2117 PFNVBOXHDDFORMATLOAD pfnHDDFormatLoad = NULL;
2118 PVBOXHDDBACKEND pBackend = NULL;
2119 char *pszPluginPath = NULL;
2120
2121 if (rc == VERR_BUFFER_OVERFLOW)
2122 {
2123 /* allocate new buffer. */
2124 RTMemFree(pPluginDirEntry);
2125 pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbPluginDirEntry);
2126 if (!pPluginDirEntry)
2127 {
2128 rc = VERR_NO_MEMORY;
2129 break;
2130 }
2131 /* Retry. */
2132 rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
2133 if (RT_FAILURE(rc))
2134 break;
2135 }
2136 else if (RT_FAILURE(rc))
2137 break;
2138
2139 /* We got the new entry. */
2140 if (!RTFS_IS_FILE(pPluginDirEntry->Info.Attr.fMode))
2141 continue;
2142
2143 /* Prepend the path to the libraries. */
2144 pszPluginPath = RTPathJoinA(szPath, pPluginDirEntry->szName);
2145 if (!pszPluginPath)
2146 {
2147 rc = VERR_NO_STR_MEMORY;
2148 break;
2149 }
2150
2151 rc = SUPR3HardenedLdrLoadPlugIn(pszPluginPath, &hPlugin, NULL);
2152 if (RT_SUCCESS(rc))
2153 {
2154 rc = RTLdrGetSymbol(hPlugin, VBOX_HDDFORMAT_LOAD_NAME, (void**)&pfnHDDFormatLoad);
2155 if (RT_FAILURE(rc) || !pfnHDDFormatLoad)
2156 {
2157 LogFunc(("error resolving the entry point %s in plugin %s, rc=%Rrc, pfnHDDFormat=%#p\n", VBOX_HDDFORMAT_LOAD_NAME, pPluginDirEntry->szName, rc, pfnHDDFormatLoad));
2158 if (RT_SUCCESS(rc))
2159 rc = VERR_SYMBOL_NOT_FOUND;
2160 }
2161
2162 if (RT_SUCCESS(rc))
2163 {
2164 /* Get the function table. */
2165 rc = pfnHDDFormatLoad(&pBackend);
2166 if (RT_SUCCESS(rc) && pBackend->cbSize == sizeof(VBOXHDDBACKEND))
2167 {
2168 pBackend->hPlugin = hPlugin;
2169 vdAddBackend(pBackend);
2170 }
2171 else
2172 LogFunc(("ignored plugin '%s': pBackend->cbSize=%d rc=%Rrc\n", pszPluginPath, pBackend->cbSize, rc));
2173 }
2174 else
2175 LogFunc(("ignored plugin '%s': rc=%Rrc\n", pszPluginPath, rc));
2176
2177 if (RT_FAILURE(rc))
2178 RTLdrClose(hPlugin);
2179 }
2180 RTStrFree(pszPluginPath);
2181 }
2182out:
2183 if (rc == VERR_NO_MORE_FILES)
2184 rc = VINF_SUCCESS;
2185 RTStrFree(pszPluginFilter);
2186 if (pPluginDirEntry)
2187 RTMemFree(pPluginDirEntry);
2188 if (pPluginDir)
2189 RTDirClose(pPluginDir);
2190 return rc;
2191#else
2192 return VINF_SUCCESS;
2193#endif
2194}
2195
2196/**
2197 * internal: scans plugin directory and loads the cache backends have been found.
2198 */
2199static int vdLoadDynamicCacheBackends()
2200{
2201#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
2202 int rc = VINF_SUCCESS;
2203 PRTDIR pPluginDir = NULL;
2204
2205 /* Enumerate plugin backends. */
2206 char szPath[RTPATH_MAX];
2207 rc = RTPathAppPrivateArch(szPath, sizeof(szPath));
2208 if (RT_FAILURE(rc))
2209 return rc;
2210
2211 /* To get all entries with VBoxHDD as prefix. */
2212 char *pszPluginFilter = RTPathJoinA(szPath, VD_CACHEFORMAT_PLUGIN_PREFIX "*");
2213 if (!pszPluginFilter)
2214 {
2215 rc = VERR_NO_STR_MEMORY;
2216 return rc;
2217 }
2218
2219 PRTDIRENTRYEX pPluginDirEntry = NULL;
2220 size_t cbPluginDirEntry = sizeof(RTDIRENTRYEX);
2221 /* The plugins are in the same directory as the other shared libs. */
2222 rc = RTDirOpenFiltered(&pPluginDir, pszPluginFilter, RTDIRFILTER_WINNT);
2223 if (RT_FAILURE(rc))
2224 {
2225 /* On Windows the above immediately signals that there are no
2226 * files matching, while on other platforms enumerating the
2227 * files below fails. Either way: no plugins. */
2228 goto out;
2229 }
2230
2231 pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
2232 if (!pPluginDirEntry)
2233 {
2234 rc = VERR_NO_MEMORY;
2235 goto out;
2236 }
2237
2238 while ((rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK)) != VERR_NO_MORE_FILES)
2239 {
2240 RTLDRMOD hPlugin = NIL_RTLDRMOD;
2241 PFNVDCACHEFORMATLOAD pfnVDCacheLoad = NULL;
2242 PVDCACHEBACKEND pBackend = NULL;
2243 char *pszPluginPath = NULL;
2244
2245 if (rc == VERR_BUFFER_OVERFLOW)
2246 {
2247 /* allocate new buffer. */
2248 RTMemFree(pPluginDirEntry);
2249 pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbPluginDirEntry);
2250 if (!pPluginDirEntry)
2251 {
2252 rc = VERR_NO_MEMORY;
2253 break;
2254 }
2255 /* Retry. */
2256 rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
2257 if (RT_FAILURE(rc))
2258 break;
2259 }
2260 else if (RT_FAILURE(rc))
2261 break;
2262
2263 /* We got the new entry. */
2264 if (!RTFS_IS_FILE(pPluginDirEntry->Info.Attr.fMode))
2265 continue;
2266
2267 /* Prepend the path to the libraries. */
2268 pszPluginPath = RTPathJoinA(szPath, pPluginDirEntry->szName);
2269 if (!pszPluginPath)
2270 {
2271 rc = VERR_NO_STR_MEMORY;
2272 break;
2273 }
2274
2275 rc = SUPR3HardenedLdrLoadPlugIn(pszPluginPath, &hPlugin, NULL);
2276 if (RT_SUCCESS(rc))
2277 {
2278 rc = RTLdrGetSymbol(hPlugin, VD_CACHEFORMAT_LOAD_NAME, (void**)&pfnVDCacheLoad);
2279 if (RT_FAILURE(rc) || !pfnVDCacheLoad)
2280 {
2281 LogFunc(("error resolving the entry point %s in plugin %s, rc=%Rrc, pfnVDCacheLoad=%#p\n",
2282 VD_CACHEFORMAT_LOAD_NAME, pPluginDirEntry->szName, rc, pfnVDCacheLoad));
2283 if (RT_SUCCESS(rc))
2284 rc = VERR_SYMBOL_NOT_FOUND;
2285 }
2286
2287 if (RT_SUCCESS(rc))
2288 {
2289 /* Get the function table. */
2290 rc = pfnVDCacheLoad(&pBackend);
2291 if (RT_SUCCESS(rc) && pBackend->cbSize == sizeof(VDCACHEBACKEND))
2292 {
2293 pBackend->hPlugin = hPlugin;
2294 vdAddCacheBackend(pBackend);
2295 }
2296 else
2297 LogFunc(("ignored plugin '%s': pBackend->cbSize=%d rc=%Rrc\n", pszPluginPath, pBackend->cbSize, rc));
2298 }
2299 else
2300 LogFunc(("ignored plugin '%s': rc=%Rrc\n", pszPluginPath, rc));
2301
2302 if (RT_FAILURE(rc))
2303 RTLdrClose(hPlugin);
2304 }
2305 RTStrFree(pszPluginPath);
2306 }
2307out:
2308 if (rc == VERR_NO_MORE_FILES)
2309 rc = VINF_SUCCESS;
2310 RTStrFree(pszPluginFilter);
2311 if (pPluginDirEntry)
2312 RTMemFree(pPluginDirEntry);
2313 if (pPluginDir)
2314 RTDirClose(pPluginDir);
2315 return rc;
2316#else
2317 return VINF_SUCCESS;
2318#endif
2319}
2320
2321/**
2322 * VD async I/O interface open callback.
2323 */
2324static int vdIOOpenFallback(void *pvUser, const char *pszLocation,
2325 uint32_t fOpen, PFNVDCOMPLETED pfnCompleted,
2326 void **ppStorage)
2327{
2328 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)RTMemAllocZ(sizeof(VDIIOFALLBACKSTORAGE));
2329
2330 if (!pStorage)
2331 return VERR_NO_MEMORY;
2332
2333 pStorage->pfnCompleted = pfnCompleted;
2334
2335 /* Open the file. */
2336 int rc = RTFileOpen(&pStorage->File, pszLocation, fOpen);
2337 if (RT_SUCCESS(rc))
2338 {
2339 *ppStorage = pStorage;
2340 return VINF_SUCCESS;
2341 }
2342
2343 RTMemFree(pStorage);
2344 return rc;
2345}
2346
2347/**
2348 * VD async I/O interface close callback.
2349 */
2350static int vdIOCloseFallback(void *pvUser, void *pvStorage)
2351{
2352 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
2353
2354 RTFileClose(pStorage->File);
2355 RTMemFree(pStorage);
2356 return VINF_SUCCESS;
2357}
2358
2359static int vdIODeleteFallback(void *pvUser, const char *pcszFilename)
2360{
2361 return RTFileDelete(pcszFilename);
2362}
2363
2364static int vdIOMoveFallback(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove)
2365{
2366 return RTFileMove(pcszSrc, pcszDst, fMove);
2367}
2368
2369static int vdIOGetFreeSpaceFallback(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace)
2370{
2371 return RTFsQuerySizes(pcszFilename, NULL, pcbFreeSpace, NULL, NULL);
2372}
2373
2374static int vdIOGetModificationTimeFallback(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime)
2375{
2376 RTFSOBJINFO info;
2377 int rc = RTPathQueryInfo(pcszFilename, &info, RTFSOBJATTRADD_NOTHING);
2378 if (RT_SUCCESS(rc))
2379 *pModificationTime = info.ModificationTime;
2380 return rc;
2381}
2382
2383/**
2384 * VD async I/O interface callback for retrieving the file size.
2385 */
2386static int vdIOGetSizeFallback(void *pvUser, void *pvStorage, uint64_t *pcbSize)
2387{
2388 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
2389
2390 return RTFileGetSize(pStorage->File, pcbSize);
2391}
2392
2393/**
2394 * VD async I/O interface callback for setting the file size.
2395 */
2396static int vdIOSetSizeFallback(void *pvUser, void *pvStorage, uint64_t cbSize)
2397{
2398 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
2399
2400 return RTFileSetSize(pStorage->File, cbSize);
2401}
2402
2403/**
2404 * VD async I/O interface callback for a synchronous write to the file.
2405 */
2406static int vdIOWriteSyncFallback(void *pvUser, void *pvStorage, uint64_t uOffset,
2407 const void *pvBuf, size_t cbWrite, size_t *pcbWritten)
2408{
2409 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
2410
2411 return RTFileWriteAt(pStorage->File, uOffset, pvBuf, cbWrite, pcbWritten);
2412}
2413
2414/**
2415 * VD async I/O interface callback for a synchronous read from the file.
2416 */
2417static int vdIOReadSyncFallback(void *pvUser, void *pvStorage, uint64_t uOffset,
2418 void *pvBuf, size_t cbRead, size_t *pcbRead)
2419{
2420 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
2421
2422 return RTFileReadAt(pStorage->File, uOffset, pvBuf, cbRead, pcbRead);
2423}
2424
2425/**
2426 * VD async I/O interface callback for a synchronous flush of the file data.
2427 */
2428static int vdIOFlushSyncFallback(void *pvUser, void *pvStorage)
2429{
2430 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
2431
2432 return RTFileFlush(pStorage->File);
2433}
2434
2435/**
2436 * VD async I/O interface callback for a asynchronous read from the file.
2437 */
2438static int vdIOReadAsyncFallback(void *pvUser, void *pStorage, uint64_t uOffset,
2439 PCRTSGSEG paSegments, size_t cSegments,
2440 size_t cbRead, void *pvCompletion,
2441 void **ppTask)
2442{
2443 return VERR_NOT_IMPLEMENTED;
2444}
2445
2446/**
2447 * VD async I/O interface callback for a asynchronous write to the file.
2448 */
2449static int vdIOWriteAsyncFallback(void *pvUser, void *pStorage, uint64_t uOffset,
2450 PCRTSGSEG paSegments, size_t cSegments,
2451 size_t cbWrite, void *pvCompletion,
2452 void **ppTask)
2453{
2454 return VERR_NOT_IMPLEMENTED;
2455}
2456
2457/**
2458 * VD async I/O interface callback for a asynchronous flush of the file data.
2459 */
2460static int vdIOFlushAsyncFallback(void *pvUser, void *pStorage,
2461 void *pvCompletion, void **ppTask)
2462{
2463 return VERR_NOT_IMPLEMENTED;
2464}
2465
2466/**
2467 * Internal - Continues an I/O context after
2468 * it was halted because of an active transfer.
2469 */
2470static int vdIoCtxContinue(PVDIOCTX pIoCtx, int rcReq)
2471{
2472 PVBOXHDD pDisk = pIoCtx->pDisk;
2473 int rc = VINF_SUCCESS;
2474
2475 if (RT_FAILURE(rcReq))
2476 ASMAtomicCmpXchgS32(&pIoCtx->rcReq, rcReq, VINF_SUCCESS);
2477
2478 if (!pIoCtx->fBlocked)
2479 {
2480 /* Continue the transfer */
2481 rc = vdIoCtxProcess(pIoCtx);
2482
2483 if ( rc == VINF_VD_ASYNC_IO_FINISHED
2484 && ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
2485 {
2486 LogFlowFunc(("I/O context completed pIoCtx=%#p\n", pIoCtx));
2487 if (pIoCtx->pIoCtxParent)
2488 {
2489 PVDIOCTX pIoCtxParent = pIoCtx->pIoCtxParent;
2490
2491 LogFlowFunc(("I/O context transferred %u bytes for the parent pIoCtxParent=%p\n",
2492 pIoCtx->Type.Child.cbTransferParent, pIoCtxParent));
2493
2494 /* Update the parent state. */
2495 Assert(!pIoCtxParent->pIoCtxParent);
2496 Assert(pIoCtx->enmTxDir == VDIOCTXTXDIR_WRITE);
2497 Assert(pIoCtxParent->cbTransferLeft >= pIoCtx->Type.Child.cbTransferParent);
2498 ASMAtomicSubU32(&pIoCtxParent->cbTransferLeft, pIoCtx->Type.Child.cbTransferParent);
2499
2500 if (RT_FAILURE(pIoCtx->rcReq))
2501 ASMAtomicCmpXchgS32(&pIoCtxParent->rcReq, pIoCtx->rcReq, VINF_SUCCESS);
2502
2503 /*
2504 * A completed child write means that we finished growing the image.
2505 * We have to process any pending writes now.
2506 */
2507 Assert(pDisk->fLocked);
2508 ASMAtomicWriteBool(&pDisk->fLocked, false);
2509
2510 /* Unblock the parent */
2511 pIoCtxParent->fBlocked = false;
2512
2513 rc = vdIoCtxProcess(pIoCtxParent);
2514
2515 if ( rc == VINF_VD_ASYNC_IO_FINISHED
2516 && ASMAtomicCmpXchgBool(&pIoCtxParent->fComplete, true, false))
2517 {
2518 LogFlowFunc(("Parent I/O context completed pIoCtxParent=%#p rcReq=%Rrc\n", pIoCtxParent, pIoCtxParent->rcReq));
2519 pIoCtxParent->Type.Root.pfnComplete(pIoCtxParent->Type.Root.pvUser1,
2520 pIoCtxParent->Type.Root.pvUser2,
2521 pIoCtxParent->rcReq);
2522 vdThreadFinishWrite(pDisk);
2523 vdIoCtxFree(pDisk, pIoCtxParent);
2524 }
2525
2526 /* Process any pending writes if the current request didn't caused another growing. */
2527 RTCritSectEnter(&pDisk->CritSect);
2528
2529 if (!RTListIsEmpty(&pDisk->ListWriteLocked) && !pDisk->fLocked)
2530 {
2531 RTLISTNODE ListTmp;
2532
2533 LogFlowFunc(("Before: pNext=%#p pPrev=%#p\n", pDisk->ListWriteLocked.pNext,
2534 pDisk->ListWriteLocked.pPrev));
2535
2536 RTListMove(&ListTmp, &pDisk->ListWriteLocked);
2537
2538 LogFlowFunc(("After: pNext=%#p pPrev=%#p\n", pDisk->ListWriteLocked.pNext,
2539 pDisk->ListWriteLocked.pPrev));
2540
2541 RTCritSectLeave(&pDisk->CritSect);
2542
2543 /* Process the list. */
2544 do
2545 {
2546 PVDIOCTXDEFERRED pDeferred = RTListGetFirst(&ListTmp, VDIOCTXDEFERRED, NodeDeferred);
2547 PVDIOCTX pIoCtxWait = pDeferred->pIoCtx;
2548
2549 AssertPtr(pIoCtxWait);
2550
2551 RTListNodeRemove(&pDeferred->NodeDeferred);
2552 RTMemFree(pDeferred);
2553
2554 Assert(!pIoCtxWait->pIoCtxParent);
2555
2556 pIoCtxWait->fBlocked = false;
2557 LogFlowFunc(("Processing waiting I/O context pIoCtxWait=%#p\n", pIoCtxWait));
2558
2559 rc = vdIoCtxProcess(pIoCtxWait);
2560 if ( rc == VINF_VD_ASYNC_IO_FINISHED
2561 && ASMAtomicCmpXchgBool(&pIoCtxWait->fComplete, true, false))
2562 {
2563 LogFlowFunc(("Waiting I/O context completed pIoCtxWait=%#p\n", pIoCtxWait));
2564 vdThreadFinishWrite(pDisk);
2565 pIoCtxWait->Type.Root.pfnComplete(pIoCtxWait->Type.Root.pvUser1,
2566 pIoCtxWait->Type.Root.pvUser2,
2567 pIoCtxWait->rcReq);
2568 vdIoCtxFree(pDisk, pIoCtxWait);
2569 }
2570 } while (!RTListIsEmpty(&ListTmp));
2571 }
2572 else
2573 RTCritSectLeave(&pDisk->CritSect);
2574 }
2575 else
2576 {
2577 if (pIoCtx->enmTxDir == VDIOCTXTXDIR_FLUSH)
2578 {
2579 vdIoCtxUnlockDisk(pDisk, pIoCtx);
2580 vdThreadFinishWrite(pDisk);
2581 }
2582 else if (pIoCtx->enmTxDir == VDIOCTXTXDIR_WRITE)
2583 vdThreadFinishWrite(pDisk);
2584 else
2585 {
2586 Assert(pIoCtx->enmTxDir == VDIOCTXTXDIR_READ);
2587 vdThreadFinishRead(pDisk);
2588 }
2589
2590 LogFlowFunc(("I/O context completed pIoCtx=%#p rcReq=%Rrc\n", pIoCtx, pIoCtx->rcReq));
2591 pIoCtx->Type.Root.pfnComplete(pIoCtx->Type.Root.pvUser1,
2592 pIoCtx->Type.Root.pvUser2,
2593 pIoCtx->rcReq);
2594 }
2595
2596 vdIoCtxFree(pDisk, pIoCtx);
2597 }
2598 }
2599
2600 return VINF_SUCCESS;
2601}
2602
2603/**
2604 * Internal - Called when user transfer completed.
2605 */
2606static int vdUserXferCompleted(PVDIOSTORAGE pIoStorage, PVDIOCTX pIoCtx,
2607 PFNVDXFERCOMPLETED pfnComplete, void *pvUser,
2608 size_t cbTransfer, int rcReq)
2609{
2610 int rc = VINF_SUCCESS;
2611 bool fIoCtxContinue = true;
2612 PVBOXHDD pDisk = pIoCtx->pDisk;
2613
2614 LogFlowFunc(("pIoStorage=%#p pIoCtx=%#p pfnComplete=%#p pvUser=%#p cbTransfer=%zu rcReq=%Rrc\n",
2615 pIoStorage, pIoCtx, pfnComplete, pvUser, cbTransfer, rcReq));
2616
2617 Assert(pIoCtx->cbTransferLeft >= cbTransfer);
2618 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbTransfer);
2619 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
2620
2621 if (pfnComplete)
2622 {
2623 RTCritSectEnter(&pDisk->CritSect);
2624 rc = pfnComplete(pIoStorage->pVDIo->pBackendData, pIoCtx, pvUser, rcReq);
2625 RTCritSectLeave(&pDisk->CritSect);
2626 }
2627
2628 if (RT_SUCCESS(rc))
2629 rc = vdIoCtxContinue(pIoCtx, rcReq);
2630 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2631 rc = VINF_SUCCESS;
2632
2633 return rc;
2634}
2635
2636/**
2637 * Internal - Called when a meta transfer completed.
2638 */
2639static int vdMetaXferCompleted(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnComplete, void *pvUser,
2640 PVDMETAXFER pMetaXfer, int rcReq)
2641{
2642 PVBOXHDD pDisk = pIoStorage->pVDIo->pDisk;
2643 RTLISTNODE ListIoCtxWaiting;
2644 bool fFlush;
2645
2646 LogFlowFunc(("pIoStorage=%#p pfnComplete=%#p pvUser=%#p pMetaXfer=%#p rcReq=%Rrc\n",
2647 pIoStorage, pfnComplete, pvUser, pMetaXfer, rcReq));
2648
2649 RTCritSectEnter(&pDisk->CritSect);
2650 fFlush = VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_FLUSH;
2651 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
2652
2653 if (!fFlush)
2654 {
2655 RTListMove(&ListIoCtxWaiting, &pMetaXfer->ListIoCtxWaiting);
2656
2657 if (RT_FAILURE(rcReq))
2658 {
2659 /* Remove from the AVL tree. */
2660 LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
2661 bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
2662 Assert(fRemoved);
2663 RTMemFree(pMetaXfer);
2664 }
2665 else
2666 {
2667 /* Increase the reference counter to make sure it doesn't go away before the last context is processed. */
2668 pMetaXfer->cRefs++;
2669 }
2670 }
2671 else
2672 RTListMove(&ListIoCtxWaiting, &pMetaXfer->ListIoCtxWaiting);
2673 RTCritSectLeave(&pDisk->CritSect);
2674
2675 /* Go through the waiting list and continue the I/O contexts. */
2676 while (!RTListIsEmpty(&ListIoCtxWaiting))
2677 {
2678 int rc = VINF_SUCCESS;
2679 bool fContinue = true;
2680 PVDIOCTXDEFERRED pDeferred = RTListGetFirst(&ListIoCtxWaiting, VDIOCTXDEFERRED, NodeDeferred);
2681 PVDIOCTX pIoCtx = pDeferred->pIoCtx;
2682 RTListNodeRemove(&pDeferred->NodeDeferred);
2683
2684 RTMemFree(pDeferred);
2685 ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
2686
2687 if (pfnComplete)
2688 {
2689 RTCritSectEnter(&pDisk->CritSect);
2690 rc = pfnComplete(pIoStorage->pVDIo->pBackendData, pIoCtx, pvUser, rcReq);
2691 RTCritSectLeave(&pDisk->CritSect);
2692 }
2693
2694 LogFlow(("Completion callback for I/O context %#p returned %Rrc\n", pIoCtx, rc));
2695
2696 if (RT_SUCCESS(rc))
2697 {
2698 rc = vdIoCtxContinue(pIoCtx, rcReq);
2699 AssertRC(rc);
2700 }
2701 else
2702 Assert(rc == VERR_VD_ASYNC_IO_IN_PROGRESS);
2703 }
2704
2705 /* Remove if not used anymore. */
2706 if (RT_SUCCESS(rcReq) && !fFlush)
2707 {
2708 RTCritSectEnter(&pDisk->CritSect);
2709 pMetaXfer->cRefs--;
2710 if (!pMetaXfer->cRefs && RTListIsEmpty(&pMetaXfer->ListIoCtxWaiting))
2711 {
2712 /* Remove from the AVL tree. */
2713 LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
2714 bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
2715 Assert(fRemoved);
2716 RTMemFree(pMetaXfer);
2717 }
2718 RTCritSectLeave(&pDisk->CritSect);
2719 }
2720 else if (fFlush)
2721 RTMemFree(pMetaXfer);
2722
2723 return VINF_SUCCESS;
2724}
2725
2726static int vdIOIntReqCompleted(void *pvUser, int rcReq)
2727{
2728 int rc = VINF_SUCCESS;
2729 PVDIOTASK pIoTask = (PVDIOTASK)pvUser;
2730 PVDIOSTORAGE pIoStorage = pIoTask->pIoStorage;
2731
2732 LogFlowFunc(("Task completed pIoTask=%#p\n", pIoTask));
2733
2734 if (!pIoTask->fMeta)
2735 rc = vdUserXferCompleted(pIoStorage, pIoTask->Type.User.pIoCtx,
2736 pIoTask->pfnComplete, pIoTask->pvUser,
2737 pIoTask->Type.User.cbTransfer, rcReq);
2738 else
2739 rc = vdMetaXferCompleted(pIoStorage, pIoTask->pfnComplete, pIoTask->pvUser,
2740 pIoTask->Type.Meta.pMetaXfer, rcReq);
2741
2742 vdIoTaskFree(pIoStorage->pVDIo->pDisk, pIoTask);
2743
2744 return rc;
2745}
2746
2747/**
2748 * VD I/O interface callback for opening a file.
2749 */
2750static int vdIOIntOpen(void *pvUser, const char *pszLocation,
2751 unsigned uOpenFlags, PPVDIOSTORAGE ppIoStorage)
2752{
2753 int rc = VINF_SUCCESS;
2754 PVDIO pVDIo = (PVDIO)pvUser;
2755 PVDIOSTORAGE pIoStorage = (PVDIOSTORAGE)RTMemAllocZ(sizeof(VDIOSTORAGE));
2756
2757 if (!pIoStorage)
2758 return VERR_NO_MEMORY;
2759
2760 /* Create the AVl tree. */
2761 pIoStorage->pTreeMetaXfers = (PAVLRFOFFTREE)RTMemAllocZ(sizeof(AVLRFOFFTREE));
2762 if (pIoStorage->pTreeMetaXfers)
2763 {
2764 rc = pVDIo->pInterfaceIOCallbacks->pfnOpen(pVDIo->pInterfaceIO->pvUser,
2765 pszLocation, uOpenFlags,
2766 vdIOIntReqCompleted,
2767 &pIoStorage->pStorage);
2768 if (RT_SUCCESS(rc))
2769 {
2770 pIoStorage->pVDIo = pVDIo;
2771 *ppIoStorage = pIoStorage;
2772 return VINF_SUCCESS;
2773 }
2774
2775 RTMemFree(pIoStorage->pTreeMetaXfers);
2776 }
2777 else
2778 rc = VERR_NO_MEMORY;
2779
2780 RTMemFree(pIoStorage);
2781 return rc;
2782}
2783
2784static int vdIOIntTreeMetaXferDestroy(PAVLRFOFFNODECORE pNode, void *pvUser)
2785{
2786 AssertMsgFailed(("Tree should be empty at this point!\n"));
2787 return VINF_SUCCESS;
2788}
2789
2790static int vdIOIntClose(void *pvUser, PVDIOSTORAGE pIoStorage)
2791{
2792 PVDIO pVDIo = (PVDIO)pvUser;
2793
2794 int rc = pVDIo->pInterfaceIOCallbacks->pfnClose(pVDIo->pInterfaceIO->pvUser,
2795 pIoStorage->pStorage);
2796 AssertRC(rc);
2797
2798 RTAvlrFileOffsetDestroy(pIoStorage->pTreeMetaXfers, vdIOIntTreeMetaXferDestroy, NULL);
2799 RTMemFree(pIoStorage->pTreeMetaXfers);
2800 RTMemFree(pIoStorage);
2801 return VINF_SUCCESS;
2802}
2803
2804static int vdIOIntDelete(void *pvUser, const char *pcszFilename)
2805{
2806 PVDIO pVDIo = (PVDIO)pvUser;
2807 return pVDIo->pInterfaceIOCallbacks->pfnDelete(pVDIo->pInterfaceIO->pvUser,
2808 pcszFilename);
2809}
2810
2811static int vdIOIntMove(void *pvUser, const char *pcszSrc, const char *pcszDst,
2812 unsigned fMove)
2813{
2814 PVDIO pVDIo = (PVDIO)pvUser;
2815 return pVDIo->pInterfaceIOCallbacks->pfnMove(pVDIo->pInterfaceIO->pvUser,
2816 pcszSrc, pcszDst, fMove);
2817}
2818
2819static int vdIOIntGetFreeSpace(void *pvUser, const char *pcszFilename,
2820 int64_t *pcbFreeSpace)
2821{
2822 PVDIO pVDIo = (PVDIO)pvUser;
2823 return pVDIo->pInterfaceIOCallbacks->pfnGetFreeSpace(pVDIo->pInterfaceIO->pvUser,
2824 pcszFilename,
2825 pcbFreeSpace);
2826}
2827
2828static int vdIOIntGetModificationTime(void *pvUser, const char *pcszFilename,
2829 PRTTIMESPEC pModificationTime)
2830{
2831 PVDIO pVDIo = (PVDIO)pvUser;
2832 return pVDIo->pInterfaceIOCallbacks->pfnGetModificationTime(pVDIo->pInterfaceIO->pvUser,
2833 pcszFilename,
2834 pModificationTime);
2835}
2836
2837static int vdIOIntGetSize(void *pvUser, PVDIOSTORAGE pIoStorage,
2838 uint64_t *pcbSize)
2839{
2840 PVDIO pVDIo = (PVDIO)pvUser;
2841 return pVDIo->pInterfaceIOCallbacks->pfnGetSize(pVDIo->pInterfaceIO->pvUser,
2842 pIoStorage->pStorage,
2843 pcbSize);
2844}
2845
2846static int vdIOIntSetSize(void *pvUser, PVDIOSTORAGE pIoStorage,
2847 uint64_t cbSize)
2848{
2849 PVDIO pVDIo = (PVDIO)pvUser;
2850
2851 return pVDIo->pInterfaceIOCallbacks->pfnSetSize(pVDIo->pInterfaceIO->pvUser,
2852 pIoStorage->pStorage,
2853 cbSize);
2854}
2855
2856static int vdIOIntWriteSync(void *pvUser, PVDIOSTORAGE pIoStorage,
2857 uint64_t uOffset, const void *pvBuf,
2858 size_t cbWrite, size_t *pcbWritten)
2859{
2860 PVDIO pVDIo = (PVDIO)pvUser;
2861
2862 return pVDIo->pInterfaceIOCallbacks->pfnWriteSync(pVDIo->pInterfaceIO->pvUser,
2863 pIoStorage->pStorage,
2864 uOffset, pvBuf, cbWrite,
2865 pcbWritten);
2866}
2867
2868static int vdIOIntReadSync(void *pvUser, PVDIOSTORAGE pIoStorage,
2869 uint64_t uOffset, void *pvBuf, size_t cbRead,
2870 size_t *pcbRead)
2871{
2872 PVDIO pVDIo = (PVDIO)pvUser;
2873 return pVDIo->pInterfaceIOCallbacks->pfnReadSync(pVDIo->pInterfaceIO->pvUser,
2874 pIoStorage->pStorage,
2875 uOffset, pvBuf, cbRead,
2876 pcbRead);
2877}
2878
2879static int vdIOIntFlushSync(void *pvUser, PVDIOSTORAGE pIoStorage)
2880{
2881 PVDIO pVDIo = (PVDIO)pvUser;
2882 return pVDIo->pInterfaceIOCallbacks->pfnFlushSync(pVDIo->pInterfaceIO->pvUser,
2883 pIoStorage->pStorage);
2884}
2885
2886static int vdIOIntReadUserAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
2887 uint64_t uOffset, PVDIOCTX pIoCtx,
2888 size_t cbRead)
2889{
2890 int rc = VINF_SUCCESS;
2891 PVDIO pVDIo = (PVDIO)pvUser;
2892 PVBOXHDD pDisk = pVDIo->pDisk;
2893
2894 LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pIoCtx=%#p cbRead=%u\n",
2895 pvUser, pIoStorage, uOffset, pIoCtx, cbRead));
2896
2897 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
2898
2899 /* Build the S/G array and spawn a new I/O task */
2900 while (cbRead)
2901 {
2902 RTSGSEG aSeg[VD_IO_TASK_SEGMENTS_MAX];
2903 unsigned cSegments = VD_IO_TASK_SEGMENTS_MAX;
2904 size_t cbTaskRead = 0;
2905
2906 cbTaskRead = RTSgBufSegArrayCreate(&pIoCtx->SgBuf, aSeg, &cSegments, cbRead);
2907
2908 AssertMsg(cbTaskRead <= cbRead, ("Invalid number of bytes to read\n"));
2909
2910 LogFlow(("Reading %u bytes into %u segments\n", cbTaskRead, cSegments));
2911
2912#ifdef RT_STRICT
2913 for (unsigned i = 0; i < cSegments; i++)
2914 AssertMsg(aSeg[i].pvSeg && !(aSeg[i].cbSeg % 512),
2915 ("Segment %u is invalid\n", i));
2916#endif
2917
2918 PVDIOTASK pIoTask = vdIoTaskUserAlloc(pIoStorage, NULL, NULL, pIoCtx, cbTaskRead);
2919
2920 if (!pIoTask)
2921 return VERR_NO_MEMORY;
2922
2923 ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
2924
2925 void *pvTask;
2926 rc = pVDIo->pInterfaceIOCallbacks->pfnReadAsync(pVDIo->pInterfaceIO->pvUser,
2927 pIoStorage->pStorage,
2928 uOffset, aSeg, cSegments,
2929 cbTaskRead, pIoTask,
2930 &pvTask);
2931 if (RT_SUCCESS(rc))
2932 {
2933 AssertMsg(cbTaskRead <= pIoCtx->cbTransferLeft, ("Impossible!\n"));
2934 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbTaskRead);
2935 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
2936 vdIoTaskFree(pDisk, pIoTask);
2937 }
2938 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
2939 {
2940 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
2941 break;
2942 }
2943
2944 uOffset += cbTaskRead;
2945 cbRead -= cbTaskRead;
2946 }
2947
2948 LogFlowFunc(("returns rc=%Rrc\n", rc));
2949 return rc;
2950}
2951
2952static int vdIOIntWriteUserAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
2953 uint64_t uOffset, PVDIOCTX pIoCtx,
2954 size_t cbWrite,
2955 PFNVDXFERCOMPLETED pfnComplete,
2956 void *pvCompleteUser)
2957{
2958 int rc = VINF_SUCCESS;
2959 PVDIO pVDIo = (PVDIO)pvUser;
2960 PVBOXHDD pDisk = pVDIo->pDisk;
2961
2962 LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pIoCtx=%#p cbWrite=%u\n",
2963 pvUser, pIoStorage, uOffset, pIoCtx, cbWrite));
2964
2965 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
2966
2967 /* Build the S/G array and spawn a new I/O task */
2968 while (cbWrite)
2969 {
2970 RTSGSEG aSeg[VD_IO_TASK_SEGMENTS_MAX];
2971 unsigned cSegments = VD_IO_TASK_SEGMENTS_MAX;
2972 size_t cbTaskWrite = 0;
2973
2974 cbTaskWrite = RTSgBufSegArrayCreate(&pIoCtx->SgBuf, aSeg, &cSegments, cbWrite);
2975
2976 AssertMsg(cbTaskWrite <= cbWrite, ("Invalid number of bytes to write\n"));
2977
2978 LogFlow(("Writing %u bytes from %u segments\n", cbTaskWrite, cSegments));
2979
2980#ifdef DEBUG
2981 for (unsigned i = 0; i < cSegments; i++)
2982 AssertMsg(aSeg[i].pvSeg && !(aSeg[i].cbSeg % 512),
2983 ("Segment %u is invalid\n", i));
2984#endif
2985
2986 PVDIOTASK pIoTask = vdIoTaskUserAlloc(pIoStorage, pfnComplete, pvCompleteUser, pIoCtx, cbTaskWrite);
2987
2988 if (!pIoTask)
2989 return VERR_NO_MEMORY;
2990
2991 ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
2992
2993 void *pvTask;
2994 rc = pVDIo->pInterfaceIOCallbacks->pfnWriteAsync(pVDIo->pInterfaceIO->pvUser,
2995 pIoStorage->pStorage,
2996 uOffset, aSeg, cSegments,
2997 cbTaskWrite, pIoTask,
2998 &pvTask);
2999 if (RT_SUCCESS(rc))
3000 {
3001 AssertMsg(cbTaskWrite <= pIoCtx->cbTransferLeft, ("Impossible!\n"));
3002 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbTaskWrite);
3003 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
3004 vdIoTaskFree(pDisk, pIoTask);
3005 }
3006 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
3007 {
3008 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
3009 break;
3010 }
3011
3012 uOffset += cbTaskWrite;
3013 cbWrite -= cbTaskWrite;
3014 }
3015
3016 return rc;
3017}
3018
3019static int vdIOIntReadMetaAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
3020 uint64_t uOffset, void *pvBuf,
3021 size_t cbRead, PVDIOCTX pIoCtx,
3022 PPVDMETAXFER ppMetaXfer,
3023 PFNVDXFERCOMPLETED pfnComplete,
3024 void *pvCompleteUser)
3025{
3026 PVDIO pVDIo = (PVDIO)pvUser;
3027 PVBOXHDD pDisk = pVDIo->pDisk;
3028 int rc = VINF_SUCCESS;
3029 RTSGSEG Seg;
3030 PVDIOTASK pIoTask;
3031 PVDMETAXFER pMetaXfer = NULL;
3032 void *pvTask = NULL;
3033
3034 LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pvBuf=%#p cbRead=%u\n",
3035 pvUser, pIoStorage, uOffset, pvBuf, cbRead));
3036
3037 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3038
3039 pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGet(pIoStorage->pTreeMetaXfers, uOffset);
3040 if (!pMetaXfer)
3041 {
3042#ifdef RT_STRICT
3043 pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGetBestFit(pIoStorage->pTreeMetaXfers, uOffset, false /* fAbove */);
3044 AssertMsg(!pMetaXfer || (pMetaXfer->Core.Key + (RTFOFF)pMetaXfer->cbMeta <= (RTFOFF)uOffset),
3045 ("Overlapping meta transfers!\n"));
3046#endif
3047
3048 /* Allocate a new meta transfer. */
3049 pMetaXfer = vdMetaXferAlloc(pIoStorage, uOffset, cbRead);
3050 if (!pMetaXfer)
3051 return VERR_NO_MEMORY;
3052
3053 pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvCompleteUser, pMetaXfer);
3054 if (!pIoTask)
3055 {
3056 RTMemFree(pMetaXfer);
3057 return VERR_NO_MEMORY;
3058 }
3059
3060 Seg.cbSeg = cbRead;
3061 Seg.pvSeg = pMetaXfer->abData;
3062
3063 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_READ);
3064 rc = pVDIo->pInterfaceIOCallbacks->pfnReadAsync(pVDIo->pInterfaceIO->pvUser,
3065 pIoStorage->pStorage,
3066 uOffset, &Seg, 1,
3067 cbRead, pIoTask,
3068 &pvTask);
3069
3070 if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
3071 {
3072 bool fInserted = RTAvlrFileOffsetInsert(pIoStorage->pTreeMetaXfers, &pMetaXfer->Core);
3073 Assert(fInserted);
3074 }
3075 else
3076 RTMemFree(pMetaXfer);
3077
3078 if (RT_SUCCESS(rc))
3079 {
3080 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
3081 vdIoTaskFree(pDisk, pIoTask);
3082 }
3083 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS && !pfnComplete)
3084 rc = VERR_VD_NOT_ENOUGH_METADATA;
3085 }
3086
3087 Assert(VALID_PTR(pMetaXfer) || RT_FAILURE(rc));
3088
3089 if (RT_SUCCESS(rc) || rc == VERR_VD_NOT_ENOUGH_METADATA || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
3090 {
3091 /* If it is pending add the request to the list. */
3092 if (VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_READ)
3093 {
3094 PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
3095 AssertPtr(pDeferred);
3096
3097 RTListInit(&pDeferred->NodeDeferred);
3098 pDeferred->pIoCtx = pIoCtx;
3099
3100 ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
3101 RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
3102 rc = VERR_VD_NOT_ENOUGH_METADATA;
3103 }
3104 else
3105 {
3106 /* Transfer the data. */
3107 pMetaXfer->cRefs++;
3108 Assert(pMetaXfer->cbMeta >= cbRead);
3109 Assert(pMetaXfer->Core.Key == (RTFOFF)uOffset);
3110 memcpy(pvBuf, pMetaXfer->abData, cbRead);
3111 *ppMetaXfer = pMetaXfer;
3112 }
3113 }
3114
3115 return rc;
3116}
3117
3118static int vdIOIntWriteMetaAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
3119 uint64_t uOffset, void *pvBuf,
3120 size_t cbWrite, PVDIOCTX pIoCtx,
3121 PFNVDXFERCOMPLETED pfnComplete,
3122 void *pvCompleteUser)
3123{
3124 PVDIO pVDIo = (PVDIO)pvUser;
3125 PVBOXHDD pDisk = pVDIo->pDisk;
3126 int rc = VINF_SUCCESS;
3127 RTSGSEG Seg;
3128 PVDIOTASK pIoTask;
3129 PVDMETAXFER pMetaXfer = NULL;
3130 bool fInTree = false;
3131 void *pvTask = NULL;
3132
3133 LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pvBuf=%#p cbWrite=%u\n",
3134 pvUser, pIoStorage, uOffset, pvBuf, cbWrite));
3135
3136 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3137
3138 pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGet(pIoStorage->pTreeMetaXfers, uOffset);
3139 if (!pMetaXfer)
3140 {
3141 /* Allocate a new meta transfer. */
3142 pMetaXfer = vdMetaXferAlloc(pIoStorage, uOffset, cbWrite);
3143 if (!pMetaXfer)
3144 return VERR_NO_MEMORY;
3145 }
3146 else
3147 {
3148 Assert(pMetaXfer->cbMeta >= cbWrite);
3149 Assert(pMetaXfer->Core.Key == (RTFOFF)uOffset);
3150 fInTree = true;
3151 }
3152
3153 Assert(VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE);
3154
3155 pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvCompleteUser, pMetaXfer);
3156 if (!pIoTask)
3157 {
3158 RTMemFree(pMetaXfer);
3159 return VERR_NO_MEMORY;
3160 }
3161
3162 memcpy(pMetaXfer->abData, pvBuf, cbWrite);
3163 Seg.cbSeg = cbWrite;
3164 Seg.pvSeg = pMetaXfer->abData;
3165
3166 ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
3167
3168 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_WRITE);
3169 rc = pVDIo->pInterfaceIOCallbacks->pfnWriteAsync(pVDIo->pInterfaceIO->pvUser,
3170 pIoStorage->pStorage,
3171 uOffset, &Seg, 1,
3172 cbWrite, pIoTask,
3173 &pvTask);
3174 if (RT_SUCCESS(rc))
3175 {
3176 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
3177 ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
3178 vdIoTaskFree(pDisk, pIoTask);
3179 if (fInTree && !pMetaXfer->cRefs)
3180 {
3181 LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
3182 bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
3183 AssertMsg(fRemoved, ("Metadata transfer wasn't removed\n"));
3184 RTMemFree(pMetaXfer);
3185 pMetaXfer = NULL;
3186 }
3187 }
3188 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
3189 {
3190 PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
3191 AssertPtr(pDeferred);
3192
3193 RTListInit(&pDeferred->NodeDeferred);
3194 pDeferred->pIoCtx = pIoCtx;
3195
3196 if (!fInTree)
3197 {
3198 bool fInserted = RTAvlrFileOffsetInsert(pIoStorage->pTreeMetaXfers, &pMetaXfer->Core);
3199 Assert(fInserted);
3200 }
3201
3202 RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
3203 }
3204 else
3205 {
3206 RTMemFree(pMetaXfer);
3207 pMetaXfer = NULL;
3208 }
3209
3210 return rc;
3211}
3212
3213static void vdIOIntMetaXferRelease(void *pvUser, PVDMETAXFER pMetaXfer)
3214{
3215 PVDIO pVDIo = (PVDIO)pvUser;
3216 PVBOXHDD pDisk = pVDIo->pDisk;
3217 PVDIOSTORAGE pIoStorage = pMetaXfer->pIoStorage;
3218
3219 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3220
3221 Assert( VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE
3222 || VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_WRITE);
3223 Assert(pMetaXfer->cRefs > 0);
3224
3225 pMetaXfer->cRefs--;
3226 if ( !pMetaXfer->cRefs
3227 && RTListIsEmpty(&pMetaXfer->ListIoCtxWaiting)
3228 && VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE)
3229 {
3230 /* Free the meta data entry. */
3231 LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
3232 bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
3233 AssertMsg(fRemoved, ("Metadata transfer wasn't removed\n"));
3234
3235 RTMemFree(pMetaXfer);
3236 }
3237}
3238
3239static int vdIOIntFlushAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
3240 PVDIOCTX pIoCtx, PFNVDXFERCOMPLETED pfnComplete,
3241 void *pvCompleteUser)
3242{
3243 PVDIO pVDIo = (PVDIO)pvUser;
3244 PVBOXHDD pDisk = pVDIo->pDisk;
3245 int rc = VINF_SUCCESS;
3246 PVDIOTASK pIoTask;
3247 PVDMETAXFER pMetaXfer = NULL;
3248 void *pvTask = NULL;
3249
3250 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3251
3252 LogFlowFunc(("pvUser=%#p pIoStorage=%#p pIoCtx=%#p\n",
3253 pvUser, pIoStorage, pIoCtx));
3254
3255 /* Allocate a new meta transfer. */
3256 pMetaXfer = vdMetaXferAlloc(pIoStorage, 0, 0);
3257 if (!pMetaXfer)
3258 return VERR_NO_MEMORY;
3259
3260 pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvUser, pMetaXfer);
3261 if (!pIoTask)
3262 {
3263 RTMemFree(pMetaXfer);
3264 return VERR_NO_MEMORY;
3265 }
3266
3267 ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
3268
3269 PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
3270 AssertPtr(pDeferred);
3271
3272 RTListInit(&pDeferred->NodeDeferred);
3273 pDeferred->pIoCtx = pIoCtx;
3274
3275 RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
3276 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_FLUSH);
3277 rc = pVDIo->pInterfaceIOCallbacks->pfnFlushAsync(pVDIo->pInterfaceIO->pvUser,
3278 pIoStorage->pStorage,
3279 pIoTask, &pvTask);
3280 if (RT_SUCCESS(rc))
3281 {
3282 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
3283 ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
3284 vdIoTaskFree(pDisk, pIoTask);
3285 RTMemFree(pDeferred);
3286 RTMemFree(pMetaXfer);
3287 }
3288 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
3289 RTMemFree(pMetaXfer);
3290
3291 return rc;
3292}
3293
3294static size_t vdIOIntIoCtxCopyTo(void *pvUser, PVDIOCTX pIoCtx,
3295 void *pvBuf, size_t cbBuf)
3296{
3297 PVDIO pVDIo = (PVDIO)pvUser;
3298 PVBOXHDD pDisk = pVDIo->pDisk;
3299 size_t cbCopied = 0;
3300
3301 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3302
3303 cbCopied = vdIoCtxCopyTo(pIoCtx, (uint8_t *)pvBuf, cbBuf);
3304 Assert(cbCopied == cbBuf);
3305
3306 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbCopied);
3307
3308 return cbCopied;
3309}
3310
3311static size_t vdIOIntIoCtxCopyFrom(void *pvUser, PVDIOCTX pIoCtx,
3312 void *pvBuf, size_t cbBuf)
3313{
3314 PVDIO pVDIo = (PVDIO)pvUser;
3315 PVBOXHDD pDisk = pVDIo->pDisk;
3316 size_t cbCopied = 0;
3317
3318 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3319
3320 cbCopied = vdIoCtxCopyFrom(pIoCtx, (uint8_t *)pvBuf, cbBuf);
3321 Assert(cbCopied == cbBuf);
3322
3323 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbCopied);
3324
3325 return cbCopied;
3326}
3327
3328static size_t vdIOIntIoCtxSet(void *pvUser, PVDIOCTX pIoCtx, int ch, size_t cb)
3329{
3330 PVDIO pVDIo = (PVDIO)pvUser;
3331 PVBOXHDD pDisk = pVDIo->pDisk;
3332 size_t cbSet = 0;
3333
3334 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3335
3336 cbSet = vdIoCtxSet(pIoCtx, ch, cb);
3337 Assert(cbSet == cb);
3338
3339 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbSet);
3340
3341 return cbSet;
3342}
3343
3344static size_t vdIOIntIoCtxSegArrayCreate(void *pvUser, PVDIOCTX pIoCtx,
3345 PRTSGSEG paSeg, unsigned *pcSeg,
3346 size_t cbData)
3347{
3348 PVDIO pVDIo = (PVDIO)pvUser;
3349 PVBOXHDD pDisk = pVDIo->pDisk;
3350 size_t cbCreated = 0;
3351
3352 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3353
3354 cbCreated = RTSgBufSegArrayCreate(&pIoCtx->SgBuf, paSeg, pcSeg, cbData);
3355 Assert(!paSeg || cbData == cbCreated);
3356
3357 return cbCreated;
3358}
3359
3360static void vdIOIntIoCtxCompleted(void *pvUser, PVDIOCTX pIoCtx, int rcReq,
3361 size_t cbCompleted)
3362{
3363 /* Continue */
3364 pIoCtx->fBlocked = false;
3365 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbCompleted);
3366
3367 /* Clear the pointer to next transfer function in case we have nothing to transfer anymore.
3368 * @todo: Find a better way to prevent vdIoCtxContinue from calling the read/write helper again. */
3369 if (!pIoCtx->cbTransferLeft)
3370 pIoCtx->pfnIoCtxTransfer = NULL;
3371
3372 vdIoCtxContinue(pIoCtx, rcReq);
3373}
3374
3375/**
3376 * VD I/O interface callback for opening a file (limited version for VDGetFormat).
3377 */
3378static int vdIOIntOpenLimited(void *pvUser, const char *pszLocation,
3379 uint32_t fOpen, PPVDIOSTORAGE ppIoStorage)
3380{
3381 int rc = VINF_SUCCESS;
3382 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3383 PVDIOSTORAGE pIoStorage = (PVDIOSTORAGE)RTMemAllocZ(sizeof(VDIOSTORAGE));
3384
3385 if (!pIoStorage)
3386 return VERR_NO_MEMORY;
3387
3388 rc = pInterfaceIOCallbacks->pfnOpen(NULL, pszLocation, fOpen,
3389 NULL, &pIoStorage->pStorage);
3390 if (RT_SUCCESS(rc))
3391 *ppIoStorage = pIoStorage;
3392 else
3393 RTMemFree(pIoStorage);
3394
3395 return rc;
3396}
3397
3398static int vdIOIntCloseLimited(void *pvUser, PVDIOSTORAGE pIoStorage)
3399{
3400 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3401 int rc = pInterfaceIOCallbacks->pfnClose(NULL, pIoStorage->pStorage);
3402 AssertRC(rc);
3403
3404 RTMemFree(pIoStorage);
3405 return VINF_SUCCESS;
3406}
3407
3408static int vdIOIntDeleteLimited(void *pvUser, const char *pcszFilename)
3409{
3410 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3411 return pInterfaceIOCallbacks->pfnDelete(NULL, pcszFilename);
3412}
3413
3414static int vdIOIntMoveLimited(void *pvUser, const char *pcszSrc,
3415 const char *pcszDst, unsigned fMove)
3416{
3417 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3418 return pInterfaceIOCallbacks->pfnMove(NULL, pcszSrc, pcszDst, fMove);
3419}
3420
3421static int vdIOIntGetFreeSpaceLimited(void *pvUser, const char *pcszFilename,
3422 int64_t *pcbFreeSpace)
3423{
3424 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3425 return pInterfaceIOCallbacks->pfnGetFreeSpace(NULL, pcszFilename, pcbFreeSpace);
3426}
3427
3428static int vdIOIntGetModificationTimeLimited(void *pvUser,
3429 const char *pcszFilename,
3430 PRTTIMESPEC pModificationTime)
3431{
3432 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3433 return pInterfaceIOCallbacks->pfnGetModificationTime(NULL, pcszFilename, pModificationTime);
3434}
3435
3436static int vdIOIntGetSizeLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
3437 uint64_t *pcbSize)
3438{
3439 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3440 return pInterfaceIOCallbacks->pfnGetSize(NULL, pIoStorage->pStorage, pcbSize);
3441}
3442
3443static int vdIOIntSetSizeLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
3444 uint64_t cbSize)
3445{
3446 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3447 return pInterfaceIOCallbacks->pfnSetSize(NULL, pIoStorage->pStorage, cbSize);
3448}
3449
3450static int vdIOIntWriteSyncLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
3451 uint64_t uOffset, const void *pvBuf,
3452 size_t cbWrite, size_t *pcbWritten)
3453{
3454 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3455 return pInterfaceIOCallbacks->pfnWriteSync(NULL, pIoStorage->pStorage, uOffset, pvBuf, cbWrite, pcbWritten);
3456}
3457
3458static int vdIOIntReadSyncLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
3459 uint64_t uOffset, void *pvBuf, size_t cbRead,
3460 size_t *pcbRead)
3461{
3462 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3463 return pInterfaceIOCallbacks->pfnReadSync(NULL, pIoStorage->pStorage, uOffset, pvBuf, cbRead, pcbRead);
3464}
3465
3466static int vdIOIntFlushSyncLimited(void *pvUser, PVDIOSTORAGE pIoStorage)
3467{
3468 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3469 return pInterfaceIOCallbacks->pfnFlushSync(NULL, pIoStorage->pStorage);
3470}
3471
3472/**
3473 * internal: send output to the log (unconditionally).
3474 */
3475int vdLogMessage(void *pvUser, const char *pszFormat, va_list args)
3476{
3477 NOREF(pvUser);
3478 RTLogPrintfV(pszFormat, args);
3479 return VINF_SUCCESS;
3480}
3481
3482DECLINLINE(int) vdMessageWrapper(PVBOXHDD pDisk, const char *pszFormat, ...)
3483{
3484 va_list va;
3485 va_start(va, pszFormat);
3486 int rc = pDisk->pInterfaceErrorCallbacks->pfnMessage(pDisk->pInterfaceError->pvUser,
3487 pszFormat, va);
3488 va_end(va);
3489 return rc;
3490}
3491
3492
3493/**
3494 * internal: adjust PCHS geometry
3495 */
3496static void vdFixupPCHSGeometry(PVDGEOMETRY pPCHS, uint64_t cbSize)
3497{
3498 /* Fix broken PCHS geometry. Can happen for two reasons: either the backend
3499 * mixes up PCHS and LCHS, or the application used to create the source
3500 * image has put garbage in it. Additionally, if the PCHS geometry covers
3501 * more than the image size, set it back to the default. */
3502 if ( pPCHS->cHeads > 16
3503 || pPCHS->cSectors > 63
3504 || pPCHS->cCylinders == 0
3505 || (uint64_t)pPCHS->cHeads * pPCHS->cSectors * pPCHS->cCylinders * 512 > cbSize)
3506 {
3507 Assert(!(RT_MIN(cbSize / 512 / 16 / 63, 16383) - (uint32_t)RT_MIN(cbSize / 512 / 16 / 63, 16383)));
3508 pPCHS->cCylinders = (uint32_t)RT_MIN(cbSize / 512 / 16 / 63, 16383);
3509 pPCHS->cHeads = 16;
3510 pPCHS->cSectors = 63;
3511 }
3512}
3513
3514/**
3515 * internal: adjust PCHS geometry
3516 */
3517static void vdFixupLCHSGeometry(PVDGEOMETRY pLCHS, uint64_t cbSize)
3518{
3519 /* Fix broken LCHS geometry. Can happen for two reasons: either the backend
3520 * mixes up PCHS and LCHS, or the application used to create the source
3521 * image has put garbage in it. The fix in this case is to clear the LCHS
3522 * geometry to trigger autodetection when it is used next. If the geometry
3523 * already says "please autodetect" (cylinders=0) keep it. */
3524 if ( ( pLCHS->cHeads > 255
3525 || pLCHS->cHeads == 0
3526 || pLCHS->cSectors > 63
3527 || pLCHS->cSectors == 0)
3528 && pLCHS->cCylinders != 0)
3529 {
3530 pLCHS->cCylinders = 0;
3531 pLCHS->cHeads = 0;
3532 pLCHS->cSectors = 0;
3533 }
3534 /* Always recompute the number of cylinders stored in the LCHS
3535 * geometry if it isn't set to "autotedetect" at the moment.
3536 * This is very useful if the destination image size is
3537 * larger or smaller than the source image size. Do not modify
3538 * the number of heads and sectors. Windows guests hate it. */
3539 if ( pLCHS->cCylinders != 0
3540 && pLCHS->cHeads != 0 /* paranoia */
3541 && pLCHS->cSectors != 0 /* paranoia */)
3542 {
3543 Assert(!(RT_MIN(cbSize / 512 / pLCHS->cHeads / pLCHS->cSectors, 1024) - (uint32_t)RT_MIN(cbSize / 512 / pLCHS->cHeads / pLCHS->cSectors, 1024)));
3544 pLCHS->cCylinders = (uint32_t)RT_MIN(cbSize / 512 / pLCHS->cHeads / pLCHS->cSectors, 1024);
3545 }
3546}
3547
3548/**
3549 * Initializes HDD backends.
3550 *
3551 * @returns VBox status code.
3552 */
3553VBOXDDU_DECL(int) VDInit(void)
3554{
3555 int rc = vdAddBackends(aStaticBackends, RT_ELEMENTS(aStaticBackends));
3556 if (RT_SUCCESS(rc))
3557 {
3558 rc = vdAddCacheBackends(aStaticCacheBackends, RT_ELEMENTS(aStaticCacheBackends));
3559 if (RT_SUCCESS(rc))
3560 {
3561 rc = vdLoadDynamicBackends();
3562 if (RT_SUCCESS(rc))
3563 rc = vdLoadDynamicCacheBackends();
3564 }
3565 }
3566 LogRel(("VDInit finished\n"));
3567 return rc;
3568}
3569
3570/**
3571 * Destroys loaded HDD backends.
3572 *
3573 * @returns VBox status code.
3574 */
3575VBOXDDU_DECL(int) VDShutdown(void)
3576{
3577 PVBOXHDDBACKEND *pBackends = g_apBackends;
3578 PVDCACHEBACKEND *pCacheBackends = g_apCacheBackends;
3579 unsigned cBackends = g_cBackends;
3580
3581 if (!pBackends)
3582 return VERR_INTERNAL_ERROR;
3583
3584 g_cBackends = 0;
3585 g_apBackends = NULL;
3586
3587#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
3588 for (unsigned i = 0; i < cBackends; i++)
3589 if (pBackends[i]->hPlugin != NIL_RTLDRMOD)
3590 RTLdrClose(pBackends[i]->hPlugin);
3591#endif
3592
3593 /* Clear the supported cache backends. */
3594 cBackends = g_cCacheBackends;
3595 g_cCacheBackends = 0;
3596 g_apCacheBackends = NULL;
3597
3598#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
3599 for (unsigned i = 0; i < cBackends; i++)
3600 if (pCacheBackends[i]->hPlugin != NIL_RTLDRMOD)
3601 RTLdrClose(pCacheBackends[i]->hPlugin);
3602#endif
3603
3604 if (pCacheBackends)
3605 RTMemFree(pCacheBackends);
3606 RTMemFree(pBackends);
3607 return VINF_SUCCESS;
3608}
3609
3610
3611/**
3612 * Lists all HDD backends and their capabilities in a caller-provided buffer.
3613 *
3614 * @returns VBox status code.
3615 * VERR_BUFFER_OVERFLOW if not enough space is passed.
3616 * @param cEntriesAlloc Number of list entries available.
3617 * @param pEntries Pointer to array for the entries.
3618 * @param pcEntriesUsed Number of entries returned.
3619 */
3620VBOXDDU_DECL(int) VDBackendInfo(unsigned cEntriesAlloc, PVDBACKENDINFO pEntries,
3621 unsigned *pcEntriesUsed)
3622{
3623 int rc = VINF_SUCCESS;
3624 PRTDIR pPluginDir = NULL;
3625 unsigned cEntries = 0;
3626
3627 LogFlowFunc(("cEntriesAlloc=%u pEntries=%#p pcEntriesUsed=%#p\n", cEntriesAlloc, pEntries, pcEntriesUsed));
3628 /* Check arguments. */
3629 AssertMsgReturn(cEntriesAlloc,
3630 ("cEntriesAlloc=%u\n", cEntriesAlloc),
3631 VERR_INVALID_PARAMETER);
3632 AssertMsgReturn(VALID_PTR(pEntries),
3633 ("pEntries=%#p\n", pEntries),
3634 VERR_INVALID_PARAMETER);
3635 AssertMsgReturn(VALID_PTR(pcEntriesUsed),
3636 ("pcEntriesUsed=%#p\n", pcEntriesUsed),
3637 VERR_INVALID_PARAMETER);
3638 if (!g_apBackends)
3639 VDInit();
3640
3641 if (cEntriesAlloc < g_cBackends)
3642 {
3643 *pcEntriesUsed = g_cBackends;
3644 return VERR_BUFFER_OVERFLOW;
3645 }
3646
3647 for (unsigned i = 0; i < g_cBackends; i++)
3648 {
3649 pEntries[i].pszBackend = g_apBackends[i]->pszBackendName;
3650 pEntries[i].uBackendCaps = g_apBackends[i]->uBackendCaps;
3651 pEntries[i].paFileExtensions = g_apBackends[i]->paFileExtensions;
3652 pEntries[i].paConfigInfo = g_apBackends[i]->paConfigInfo;
3653 pEntries[i].pfnComposeLocation = g_apBackends[i]->pfnComposeLocation;
3654 pEntries[i].pfnComposeName = g_apBackends[i]->pfnComposeName;
3655 }
3656
3657 LogFlowFunc(("returns %Rrc *pcEntriesUsed=%u\n", rc, cEntries));
3658 *pcEntriesUsed = g_cBackends;
3659 return rc;
3660}
3661
3662/**
3663 * Lists the capabilities of a backend identified by its name.
3664 *
3665 * @returns VBox status code.
3666 * @param pszBackend The backend name.
3667 * @param pEntries Pointer to an entry.
3668 */
3669VBOXDDU_DECL(int) VDBackendInfoOne(const char *pszBackend, PVDBACKENDINFO pEntry)
3670{
3671 LogFlowFunc(("pszBackend=%#p pEntry=%#p\n", pszBackend, pEntry));
3672 /* Check arguments. */
3673 AssertMsgReturn(VALID_PTR(pszBackend),
3674 ("pszBackend=%#p\n", pszBackend),
3675 VERR_INVALID_PARAMETER);
3676 AssertMsgReturn(VALID_PTR(pEntry),
3677 ("pEntry=%#p\n", pEntry),
3678 VERR_INVALID_PARAMETER);
3679 if (!g_apBackends)
3680 VDInit();
3681
3682 /* Go through loaded backends. */
3683 for (unsigned i = 0; i < g_cBackends; i++)
3684 {
3685 if (!RTStrICmp(pszBackend, g_apBackends[i]->pszBackendName))
3686 {
3687 pEntry->pszBackend = g_apBackends[i]->pszBackendName;
3688 pEntry->uBackendCaps = g_apBackends[i]->uBackendCaps;
3689 pEntry->paFileExtensions = g_apBackends[i]->paFileExtensions;
3690 pEntry->paConfigInfo = g_apBackends[i]->paConfigInfo;
3691 return VINF_SUCCESS;
3692 }
3693 }
3694
3695 return VERR_NOT_FOUND;
3696}
3697
3698/**
3699 * Allocates and initializes an empty HDD container.
3700 * No image files are opened.
3701 *
3702 * @returns VBox status code.
3703 * @param pVDIfsDisk Pointer to the per-disk VD interface list.
3704 * @param enmType Type of the image container.
3705 * @param ppDisk Where to store the reference to HDD container.
3706 */
3707VBOXDDU_DECL(int) VDCreate(PVDINTERFACE pVDIfsDisk, VDTYPE enmType, PVBOXHDD *ppDisk)
3708{
3709 int rc = VINF_SUCCESS;
3710 PVBOXHDD pDisk = NULL;
3711
3712 LogFlowFunc(("pVDIfsDisk=%#p\n", pVDIfsDisk));
3713 do
3714 {
3715 /* Check arguments. */
3716 AssertMsgBreakStmt(VALID_PTR(ppDisk),
3717 ("ppDisk=%#p\n", ppDisk),
3718 rc = VERR_INVALID_PARAMETER);
3719
3720 pDisk = (PVBOXHDD)RTMemAllocZ(sizeof(VBOXHDD));
3721 if (pDisk)
3722 {
3723 pDisk->u32Signature = VBOXHDDDISK_SIGNATURE;
3724 pDisk->enmType = enmType;
3725 pDisk->cImages = 0;
3726 pDisk->pBase = NULL;
3727 pDisk->pLast = NULL;
3728 pDisk->cbSize = 0;
3729 pDisk->PCHSGeometry.cCylinders = 0;
3730 pDisk->PCHSGeometry.cHeads = 0;
3731 pDisk->PCHSGeometry.cSectors = 0;
3732 pDisk->LCHSGeometry.cCylinders = 0;
3733 pDisk->LCHSGeometry.cHeads = 0;
3734 pDisk->LCHSGeometry.cSectors = 0;
3735 pDisk->pVDIfsDisk = pVDIfsDisk;
3736 pDisk->pInterfaceError = NULL;
3737 pDisk->pInterfaceErrorCallbacks = NULL;
3738 pDisk->pInterfaceThreadSync = NULL;
3739 pDisk->pInterfaceThreadSyncCallbacks = NULL;
3740 pDisk->fLocked = false;
3741 RTListInit(&pDisk->ListWriteLocked);
3742
3743 /* Create the I/O ctx cache */
3744 rc = RTMemCacheCreate(&pDisk->hMemCacheIoCtx, sizeof(VDIOCTX), 0, UINT32_MAX,
3745 NULL, NULL, NULL, 0);
3746 if (RT_FAILURE(rc))
3747 {
3748 RTMemFree(pDisk);
3749 break;
3750 }
3751
3752 /* Create the I/O task cache */
3753 rc = RTMemCacheCreate(&pDisk->hMemCacheIoTask, sizeof(VDIOTASK), 0, UINT32_MAX,
3754 NULL, NULL, NULL, 0);
3755 if (RT_FAILURE(rc))
3756 {
3757 RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
3758 RTMemFree(pDisk);
3759 break;
3760 }
3761
3762 /* Create critical section. */
3763 rc = RTCritSectInit(&pDisk->CritSect);
3764 if (RT_FAILURE(rc))
3765 {
3766 RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
3767 RTMemCacheDestroy(pDisk->hMemCacheIoTask);
3768 RTMemFree(pDisk);
3769 break;
3770 }
3771
3772 pDisk->pInterfaceError = VDInterfaceGet(pVDIfsDisk, VDINTERFACETYPE_ERROR);
3773 if (pDisk->pInterfaceError)
3774 pDisk->pInterfaceErrorCallbacks = VDGetInterfaceError(pDisk->pInterfaceError);
3775
3776 pDisk->pInterfaceThreadSync = VDInterfaceGet(pVDIfsDisk, VDINTERFACETYPE_THREADSYNC);
3777 if (pDisk->pInterfaceThreadSync)
3778 pDisk->pInterfaceThreadSyncCallbacks = VDGetInterfaceThreadSync(pDisk->pInterfaceThreadSync);
3779
3780 /* Create fallback I/O callback table */
3781 pDisk->VDIIOCallbacks.cbSize = sizeof(VDINTERFACEIO);
3782 pDisk->VDIIOCallbacks.enmInterface = VDINTERFACETYPE_IO;
3783 pDisk->VDIIOCallbacks.pfnOpen = vdIOOpenFallback;
3784 pDisk->VDIIOCallbacks.pfnClose = vdIOCloseFallback;
3785 pDisk->VDIIOCallbacks.pfnDelete = vdIODeleteFallback;
3786 pDisk->VDIIOCallbacks.pfnMove = vdIOMoveFallback;
3787 pDisk->VDIIOCallbacks.pfnGetFreeSpace = vdIOGetFreeSpaceFallback;
3788 pDisk->VDIIOCallbacks.pfnGetModificationTime = vdIOGetModificationTimeFallback;
3789 pDisk->VDIIOCallbacks.pfnGetSize = vdIOGetSizeFallback;
3790 pDisk->VDIIOCallbacks.pfnSetSize = vdIOSetSizeFallback;
3791 pDisk->VDIIOCallbacks.pfnReadSync = vdIOReadSyncFallback;
3792 pDisk->VDIIOCallbacks.pfnWriteSync = vdIOWriteSyncFallback;
3793 pDisk->VDIIOCallbacks.pfnFlushSync = vdIOFlushSyncFallback;
3794 pDisk->VDIIOCallbacks.pfnReadAsync = vdIOReadAsyncFallback;
3795 pDisk->VDIIOCallbacks.pfnWriteAsync = vdIOWriteAsyncFallback;
3796 pDisk->VDIIOCallbacks.pfnFlushAsync = vdIOFlushAsyncFallback;
3797
3798 /*
3799 * Create the internal I/O callback table.
3800 * The interface is per-image but no need to duplicate the
3801 * callback table every time.
3802 */
3803 pDisk->VDIIOIntCallbacks.cbSize = sizeof(VDINTERFACEIOINT);
3804 pDisk->VDIIOIntCallbacks.enmInterface = VDINTERFACETYPE_IOINT;
3805 pDisk->VDIIOIntCallbacks.pfnOpen = vdIOIntOpen;
3806 pDisk->VDIIOIntCallbacks.pfnClose = vdIOIntClose;
3807 pDisk->VDIIOIntCallbacks.pfnDelete = vdIOIntDelete;
3808 pDisk->VDIIOIntCallbacks.pfnMove = vdIOIntMove;
3809 pDisk->VDIIOIntCallbacks.pfnGetFreeSpace = vdIOIntGetFreeSpace;
3810 pDisk->VDIIOIntCallbacks.pfnGetModificationTime = vdIOIntGetModificationTime;
3811 pDisk->VDIIOIntCallbacks.pfnGetSize = vdIOIntGetSize;
3812 pDisk->VDIIOIntCallbacks.pfnSetSize = vdIOIntSetSize;
3813 pDisk->VDIIOIntCallbacks.pfnReadSync = vdIOIntReadSync;
3814 pDisk->VDIIOIntCallbacks.pfnWriteSync = vdIOIntWriteSync;
3815 pDisk->VDIIOIntCallbacks.pfnFlushSync = vdIOIntFlushSync;
3816 pDisk->VDIIOIntCallbacks.pfnReadUserAsync = vdIOIntReadUserAsync;
3817 pDisk->VDIIOIntCallbacks.pfnWriteUserAsync = vdIOIntWriteUserAsync;
3818 pDisk->VDIIOIntCallbacks.pfnReadMetaAsync = vdIOIntReadMetaAsync;
3819 pDisk->VDIIOIntCallbacks.pfnWriteMetaAsync = vdIOIntWriteMetaAsync;
3820 pDisk->VDIIOIntCallbacks.pfnMetaXferRelease = vdIOIntMetaXferRelease;
3821 pDisk->VDIIOIntCallbacks.pfnFlushAsync = vdIOIntFlushAsync;
3822 pDisk->VDIIOIntCallbacks.pfnIoCtxCopyFrom = vdIOIntIoCtxCopyFrom;
3823 pDisk->VDIIOIntCallbacks.pfnIoCtxCopyTo = vdIOIntIoCtxCopyTo;
3824 pDisk->VDIIOIntCallbacks.pfnIoCtxSet = vdIOIntIoCtxSet;
3825 pDisk->VDIIOIntCallbacks.pfnIoCtxSegArrayCreate = vdIOIntIoCtxSegArrayCreate;
3826 pDisk->VDIIOIntCallbacks.pfnIoCtxCompleted = vdIOIntIoCtxCompleted;
3827
3828 *ppDisk = pDisk;
3829 }
3830 else
3831 {
3832 rc = VERR_NO_MEMORY;
3833 break;
3834 }
3835 } while (0);
3836
3837 LogFlowFunc(("returns %Rrc (pDisk=%#p)\n", rc, pDisk));
3838 return rc;
3839}
3840
3841/**
3842 * Destroys HDD container.
3843 * If container has opened image files they will be closed.
3844 *
3845 * @param pDisk Pointer to HDD container.
3846 */
3847VBOXDDU_DECL(void) VDDestroy(PVBOXHDD pDisk)
3848{
3849 LogFlowFunc(("pDisk=%#p\n", pDisk));
3850 do
3851 {
3852 /* sanity check */
3853 AssertPtrBreak(pDisk);
3854 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3855 VDCloseAll(pDisk);
3856 RTCritSectDelete(&pDisk->CritSect);
3857 RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
3858 RTMemCacheDestroy(pDisk->hMemCacheIoTask);
3859 RTMemFree(pDisk);
3860 } while (0);
3861 LogFlowFunc(("returns\n"));
3862}
3863
3864/**
3865 * Try to get the backend name which can use this image.
3866 *
3867 * @returns VBox status code.
3868 * VINF_SUCCESS if a plugin was found.
3869 * ppszFormat contains the string which can be used as backend name.
3870 * VERR_NOT_SUPPORTED if no backend was found.
3871 * @param pVDIfsDisk Pointer to the per-disk VD interface list.
3872 * @param pVDIfsImage Pointer to the per-image VD interface list.
3873 * @param pszFilename Name of the image file for which the backend is queried.
3874 * @param ppszFormat Receives pointer of the UTF-8 string which contains the format name.
3875 * The returned pointer must be freed using RTStrFree().
3876 */
3877VBOXDDU_DECL(int) VDGetFormat(PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
3878 const char *pszFilename, char **ppszFormat, VDTYPE *penmType)
3879{
3880 int rc = VERR_NOT_SUPPORTED;
3881 VDINTERFACEIOINT VDIIOIntCallbacks;
3882 VDINTERFACE VDIIOInt;
3883 VDINTERFACEIO VDIIOCallbacksFallback;
3884 PVDINTERFACE pInterfaceIO;
3885 PVDINTERFACEIO pInterfaceIOCallbacks;
3886
3887 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
3888 /* Check arguments. */
3889 AssertMsgReturn(VALID_PTR(pszFilename) && *pszFilename,
3890 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
3891 VERR_INVALID_PARAMETER);
3892 AssertMsgReturn(VALID_PTR(ppszFormat),
3893 ("ppszFormat=%#p\n", ppszFormat),
3894 VERR_INVALID_PARAMETER);
3895 AssertMsgReturn(VALID_PTR(ppszFormat),
3896 ("penmType=%#p\n", penmType),
3897 VERR_INVALID_PARAMETER);
3898
3899 if (!g_apBackends)
3900 VDInit();
3901
3902 pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
3903 if (!pInterfaceIO)
3904 {
3905 /*
3906 * Caller doesn't provide an I/O interface, create our own using the
3907 * native file API.
3908 */
3909 VDIIOCallbacksFallback.cbSize = sizeof(VDINTERFACEIO);
3910 VDIIOCallbacksFallback.enmInterface = VDINTERFACETYPE_IO;
3911 VDIIOCallbacksFallback.pfnOpen = vdIOOpenFallback;
3912 VDIIOCallbacksFallback.pfnClose = vdIOCloseFallback;
3913 VDIIOCallbacksFallback.pfnDelete = vdIODeleteFallback;
3914 VDIIOCallbacksFallback.pfnMove = vdIOMoveFallback;
3915 VDIIOCallbacksFallback.pfnGetFreeSpace = vdIOGetFreeSpaceFallback;
3916 VDIIOCallbacksFallback.pfnGetModificationTime = vdIOGetModificationTimeFallback;
3917 VDIIOCallbacksFallback.pfnGetSize = vdIOGetSizeFallback;
3918 VDIIOCallbacksFallback.pfnSetSize = vdIOSetSizeFallback;
3919 VDIIOCallbacksFallback.pfnReadSync = vdIOReadSyncFallback;
3920 VDIIOCallbacksFallback.pfnWriteSync = vdIOWriteSyncFallback;
3921 VDIIOCallbacksFallback.pfnFlushSync = vdIOFlushSyncFallback;
3922 pInterfaceIOCallbacks = &VDIIOCallbacksFallback;
3923 }
3924 else
3925 pInterfaceIOCallbacks = VDGetInterfaceIO(pInterfaceIO);
3926
3927 /* Set up the internal I/O interface. */
3928 AssertReturn(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
3929 VERR_INVALID_PARAMETER);
3930 VDIIOIntCallbacks.cbSize = sizeof(VDINTERFACEIOINT);
3931 VDIIOIntCallbacks.enmInterface = VDINTERFACETYPE_IOINT;
3932 VDIIOIntCallbacks.pfnOpen = vdIOIntOpenLimited;
3933 VDIIOIntCallbacks.pfnClose = vdIOIntCloseLimited;
3934 VDIIOIntCallbacks.pfnDelete = vdIOIntDeleteLimited;
3935 VDIIOIntCallbacks.pfnMove = vdIOIntMoveLimited;
3936 VDIIOIntCallbacks.pfnGetFreeSpace = vdIOIntGetFreeSpaceLimited;
3937 VDIIOIntCallbacks.pfnGetModificationTime = vdIOIntGetModificationTimeLimited;
3938 VDIIOIntCallbacks.pfnGetSize = vdIOIntGetSizeLimited;
3939 VDIIOIntCallbacks.pfnSetSize = vdIOIntSetSizeLimited;
3940 VDIIOIntCallbacks.pfnReadSync = vdIOIntReadSyncLimited;
3941 VDIIOIntCallbacks.pfnWriteSync = vdIOIntWriteSyncLimited;
3942 VDIIOIntCallbacks.pfnFlushSync = vdIOIntFlushSyncLimited;
3943 VDIIOIntCallbacks.pfnReadUserAsync = NULL;
3944 VDIIOIntCallbacks.pfnWriteUserAsync = NULL;
3945 VDIIOIntCallbacks.pfnReadMetaAsync = NULL;
3946 VDIIOIntCallbacks.pfnWriteMetaAsync = NULL;
3947 VDIIOIntCallbacks.pfnFlushAsync = NULL;
3948 rc = VDInterfaceAdd(&VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
3949 &VDIIOIntCallbacks, pInterfaceIOCallbacks, &pVDIfsImage);
3950 AssertRC(rc);
3951
3952 /* Find the backend supporting this file format. */
3953 for (unsigned i = 0; i < g_cBackends; i++)
3954 {
3955 if (g_apBackends[i]->pfnCheckIfValid)
3956 {
3957 rc = g_apBackends[i]->pfnCheckIfValid(pszFilename, pVDIfsDisk,
3958 pVDIfsImage, penmType);
3959 if ( RT_SUCCESS(rc)
3960 /* The correct backend has been found, but there is a small
3961 * incompatibility so that the file cannot be used. Stop here
3962 * and signal success - the actual open will of course fail,
3963 * but that will create a really sensible error message. */
3964 || ( rc != VERR_VD_GEN_INVALID_HEADER
3965 && rc != VERR_VD_VDI_INVALID_HEADER
3966 && rc != VERR_VD_VMDK_INVALID_HEADER
3967 && rc != VERR_VD_ISCSI_INVALID_HEADER
3968 && rc != VERR_VD_VHD_INVALID_HEADER
3969 && rc != VERR_VD_RAW_INVALID_HEADER
3970 && rc != VERR_VD_PARALLELS_INVALID_HEADER
3971 && rc != VERR_VD_DMG_INVALID_HEADER))
3972 {
3973 /* Copy the name into the new string. */
3974 char *pszFormat = RTStrDup(g_apBackends[i]->pszBackendName);
3975 if (!pszFormat)
3976 {
3977 rc = VERR_NO_MEMORY;
3978 break;
3979 }
3980 *ppszFormat = pszFormat;
3981 rc = VINF_SUCCESS;
3982 break;
3983 }
3984 rc = VERR_NOT_SUPPORTED;
3985 }
3986 }
3987
3988 /* Try the cache backends. */
3989 if (rc == VERR_NOT_SUPPORTED)
3990 {
3991 for (unsigned i = 0; i < g_cCacheBackends; i++)
3992 {
3993 if (g_apCacheBackends[i]->pfnProbe)
3994 {
3995 rc = g_apCacheBackends[i]->pfnProbe(pszFilename, pVDIfsDisk,
3996 pVDIfsImage);
3997 if ( RT_SUCCESS(rc)
3998 || (rc != VERR_VD_GEN_INVALID_HEADER))
3999 {
4000 /* Copy the name into the new string. */
4001 char *pszFormat = RTStrDup(g_apBackends[i]->pszBackendName);
4002 if (!pszFormat)
4003 {
4004 rc = VERR_NO_MEMORY;
4005 break;
4006 }
4007 *ppszFormat = pszFormat;
4008 rc = VINF_SUCCESS;
4009 break;
4010 }
4011 rc = VERR_NOT_SUPPORTED;
4012 }
4013 }
4014 }
4015
4016 LogFlowFunc(("returns %Rrc *ppszFormat=\"%s\"\n", rc, *ppszFormat));
4017 return rc;
4018}
4019
4020/**
4021 * Opens an image file.
4022 *
4023 * The first opened image file in HDD container must have a base image type,
4024 * others (next opened images) must be a differencing or undo images.
4025 * Linkage is checked for differencing image to be in consistence with the previously opened image.
4026 * When another differencing image is opened and the last image was opened in read/write access
4027 * mode, then the last image is reopened in read-only with deny write sharing mode. This allows
4028 * other processes to use images in read-only mode too.
4029 *
4030 * Note that the image is opened in read-only mode if a read/write open is not possible.
4031 * Use VDIsReadOnly to check open mode.
4032 *
4033 * @returns VBox status code.
4034 * @param pDisk Pointer to HDD container.
4035 * @param pszBackend Name of the image file backend to use.
4036 * @param pszFilename Name of the image file to open.
4037 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
4038 * @param pVDIfsImage Pointer to the per-image VD interface list.
4039 */
4040VBOXDDU_DECL(int) VDOpen(PVBOXHDD pDisk, const char *pszBackend,
4041 const char *pszFilename, unsigned uOpenFlags,
4042 PVDINTERFACE pVDIfsImage)
4043{
4044 int rc = VINF_SUCCESS;
4045 int rc2;
4046 bool fLockWrite = false;
4047 PVDIMAGE pImage = NULL;
4048
4049 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x, pVDIfsImage=%#p\n",
4050 pDisk, pszBackend, pszFilename, uOpenFlags, pVDIfsImage));
4051
4052 do
4053 {
4054 /* sanity check */
4055 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4056 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4057
4058 /* Check arguments. */
4059 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
4060 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
4061 rc = VERR_INVALID_PARAMETER);
4062 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
4063 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
4064 rc = VERR_INVALID_PARAMETER);
4065 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
4066 ("uOpenFlags=%#x\n", uOpenFlags),
4067 rc = VERR_INVALID_PARAMETER);
4068
4069 /* Set up image descriptor. */
4070 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
4071 if (!pImage)
4072 {
4073 rc = VERR_NO_MEMORY;
4074 break;
4075 }
4076 pImage->pszFilename = RTStrDup(pszFilename);
4077 if (!pImage->pszFilename)
4078 {
4079 rc = VERR_NO_MEMORY;
4080 break;
4081 }
4082
4083 pImage->VDIo.pDisk = pDisk;
4084 pImage->pVDIfsImage = pVDIfsImage;
4085
4086 rc = vdFindBackend(pszBackend, &pImage->Backend);
4087 if (RT_FAILURE(rc))
4088 break;
4089 if (!pImage->Backend)
4090 {
4091 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4092 N_("VD: unknown backend name '%s'"), pszBackend);
4093 break;
4094 }
4095
4096 /* Set up the I/O interface. */
4097 pImage->VDIo.pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
4098 if (pImage->VDIo.pInterfaceIO)
4099 pImage->VDIo.pInterfaceIOCallbacks = VDGetInterfaceIO(pImage->VDIo.pInterfaceIO);
4100 else
4101 {
4102 rc = VDInterfaceAdd(&pImage->VDIo.VDIIO, "VD_IO", VDINTERFACETYPE_IO,
4103 &pDisk->VDIIOCallbacks, pDisk, &pVDIfsImage);
4104 pImage->VDIo.pInterfaceIO = &pImage->VDIo.VDIIO;
4105 pImage->VDIo.pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
4106 }
4107
4108 /* Set up the internal I/O interface. */
4109 AssertBreakStmt(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
4110 rc = VERR_INVALID_PARAMETER);
4111 rc = VDInterfaceAdd(&pImage->VDIo.VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
4112 &pDisk->VDIIOIntCallbacks, &pImage->VDIo, &pImage->pVDIfsImage);
4113 AssertRC(rc);
4114
4115 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
4116 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
4117 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
4118 pDisk->pVDIfsDisk,
4119 pImage->pVDIfsImage,
4120 pDisk->enmType,
4121 &pImage->pBackendData);
4122 /* If the open in read-write mode failed, retry in read-only mode. */
4123 if (RT_FAILURE(rc))
4124 {
4125 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
4126 && ( rc == VERR_ACCESS_DENIED
4127 || rc == VERR_PERMISSION_DENIED
4128 || rc == VERR_WRITE_PROTECT
4129 || rc == VERR_SHARING_VIOLATION
4130 || rc == VERR_FILE_LOCK_FAILED))
4131 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
4132 (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
4133 | VD_OPEN_FLAGS_READONLY,
4134 pDisk->pVDIfsDisk,
4135 pImage->pVDIfsImage,
4136 pDisk->enmType,
4137 &pImage->pBackendData);
4138 if (RT_FAILURE(rc))
4139 {
4140 rc = vdError(pDisk, rc, RT_SRC_POS,
4141 N_("VD: error %Rrc opening image file '%s'"), rc, pszFilename);
4142 break;
4143 }
4144 }
4145
4146 /* Lock disk for writing, as we modify pDisk information below. */
4147 rc2 = vdThreadStartWrite(pDisk);
4148 AssertRC(rc2);
4149 fLockWrite = true;
4150
4151 pImage->VDIo.pBackendData = pImage->pBackendData;
4152
4153 /* Check image type. As the image itself has only partial knowledge
4154 * whether it's a base image or not, this info is derived here. The
4155 * base image can be fixed or normal, all others must be normal or
4156 * diff images. Some image formats don't distinguish between normal
4157 * and diff images, so this must be corrected here. */
4158 unsigned uImageFlags;
4159 uImageFlags = pImage->Backend->pfnGetImageFlags(pImage->pBackendData);
4160 if (RT_FAILURE(rc))
4161 uImageFlags = VD_IMAGE_FLAGS_NONE;
4162 if ( RT_SUCCESS(rc)
4163 && !(uOpenFlags & VD_OPEN_FLAGS_INFO))
4164 {
4165 if ( pDisk->cImages == 0
4166 && (uImageFlags & VD_IMAGE_FLAGS_DIFF))
4167 {
4168 rc = VERR_VD_INVALID_TYPE;
4169 break;
4170 }
4171 else if (pDisk->cImages != 0)
4172 {
4173 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
4174 {
4175 rc = VERR_VD_INVALID_TYPE;
4176 break;
4177 }
4178 else
4179 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
4180 }
4181 }
4182
4183 /* Ensure we always get correct diff information, even if the backend
4184 * doesn't actually have a stored flag for this. It must not return
4185 * bogus information for the parent UUID if it is not a diff image. */
4186 RTUUID parentUuid;
4187 RTUuidClear(&parentUuid);
4188 rc2 = pImage->Backend->pfnGetParentUuid(pImage->pBackendData, &parentUuid);
4189 if (RT_SUCCESS(rc2) && !RTUuidIsNull(&parentUuid))
4190 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
4191
4192 pImage->uImageFlags = uImageFlags;
4193
4194 /* Force sane optimization settings. It's not worth avoiding writes
4195 * to fixed size images. The overhead would have almost no payback. */
4196 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
4197 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
4198
4199 /** @todo optionally check UUIDs */
4200
4201 /* Cache disk information. */
4202 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
4203
4204 /* Cache PCHS geometry. */
4205 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
4206 &pDisk->PCHSGeometry);
4207 if (RT_FAILURE(rc2))
4208 {
4209 pDisk->PCHSGeometry.cCylinders = 0;
4210 pDisk->PCHSGeometry.cHeads = 0;
4211 pDisk->PCHSGeometry.cSectors = 0;
4212 }
4213 else
4214 {
4215 /* Make sure the PCHS geometry is properly clipped. */
4216 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
4217 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
4218 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
4219 }
4220
4221 /* Cache LCHS geometry. */
4222 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
4223 &pDisk->LCHSGeometry);
4224 if (RT_FAILURE(rc2))
4225 {
4226 pDisk->LCHSGeometry.cCylinders = 0;
4227 pDisk->LCHSGeometry.cHeads = 0;
4228 pDisk->LCHSGeometry.cSectors = 0;
4229 }
4230 else
4231 {
4232 /* Make sure the LCHS geometry is properly clipped. */
4233 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
4234 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
4235 }
4236
4237 if (pDisk->cImages != 0)
4238 {
4239 /* Switch previous image to read-only mode. */
4240 unsigned uOpenFlagsPrevImg;
4241 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
4242 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
4243 {
4244 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
4245 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pBackendData, uOpenFlagsPrevImg);
4246 }
4247 }
4248
4249 if (RT_SUCCESS(rc))
4250 {
4251 /* Image successfully opened, make it the last image. */
4252 vdAddImageToList(pDisk, pImage);
4253 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
4254 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
4255 }
4256 else
4257 {
4258 /* Error detected, but image opened. Close image. */
4259 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, false);
4260 AssertRC(rc2);
4261 pImage->pBackendData = NULL;
4262 }
4263 } while (0);
4264
4265 if (RT_UNLIKELY(fLockWrite))
4266 {
4267 rc2 = vdThreadFinishWrite(pDisk);
4268 AssertRC(rc2);
4269 }
4270
4271 if (RT_FAILURE(rc))
4272 {
4273 if (pImage)
4274 {
4275 if (pImage->pszFilename)
4276 RTStrFree(pImage->pszFilename);
4277 RTMemFree(pImage);
4278 }
4279 }
4280
4281 LogFlowFunc(("returns %Rrc\n", rc));
4282 return rc;
4283}
4284
4285/**
4286 * Opens a cache image.
4287 *
4288 * @return VBox status code.
4289 * @param pDisk Pointer to the HDD container which should use the cache image.
4290 * @param pszBackend Name of the cache file backend to use (case insensitive).
4291 * @param pszFilename Name of the cache image to open.
4292 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
4293 * @param pVDIfsCache Pointer to the per-cache VD interface list.
4294 */
4295VBOXDDU_DECL(int) VDCacheOpen(PVBOXHDD pDisk, const char *pszBackend,
4296 const char *pszFilename, unsigned uOpenFlags,
4297 PVDINTERFACE pVDIfsCache)
4298{
4299 int rc = VINF_SUCCESS;
4300 int rc2;
4301 bool fLockWrite = false;
4302 PVDCACHE pCache = NULL;
4303
4304 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x, pVDIfsCache=%#p\n",
4305 pDisk, pszBackend, pszFilename, uOpenFlags, pVDIfsCache));
4306
4307 do
4308 {
4309 /* sanity check */
4310 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4311 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4312
4313 /* Check arguments. */
4314 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
4315 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
4316 rc = VERR_INVALID_PARAMETER);
4317 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
4318 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
4319 rc = VERR_INVALID_PARAMETER);
4320 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
4321 ("uOpenFlags=%#x\n", uOpenFlags),
4322 rc = VERR_INVALID_PARAMETER);
4323
4324 /* Set up image descriptor. */
4325 pCache = (PVDCACHE)RTMemAllocZ(sizeof(VDCACHE));
4326 if (!pCache)
4327 {
4328 rc = VERR_NO_MEMORY;
4329 break;
4330 }
4331 pCache->pszFilename = RTStrDup(pszFilename);
4332 if (!pCache->pszFilename)
4333 {
4334 rc = VERR_NO_MEMORY;
4335 break;
4336 }
4337
4338 pCache->VDIo.pDisk = pDisk;
4339 pCache->pVDIfsCache = pVDIfsCache;
4340
4341 rc = vdFindCacheBackend(pszBackend, &pCache->Backend);
4342 if (RT_FAILURE(rc))
4343 break;
4344 if (!pCache->Backend)
4345 {
4346 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4347 N_("VD: unknown backend name '%s'"), pszBackend);
4348 break;
4349 }
4350
4351 /* Set up the I/O interface. */
4352 pCache->VDIo.pInterfaceIO = VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IO);
4353 if (pCache->VDIo.pInterfaceIO)
4354 pCache->VDIo.pInterfaceIOCallbacks = VDGetInterfaceIO(pCache->VDIo.pInterfaceIO);
4355 else
4356 {
4357 rc = VDInterfaceAdd(&pCache->VDIo.VDIIO, "VD_IO", VDINTERFACETYPE_IO,
4358 &pDisk->VDIIOCallbacks, pDisk, &pVDIfsCache);
4359 pCache->VDIo.pInterfaceIO = &pCache->VDIo.VDIIO;
4360 pCache->VDIo.pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
4361 }
4362
4363 /* Set up the internal I/O interface. */
4364 AssertBreakStmt(!VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IOINT),
4365 rc = VERR_INVALID_PARAMETER);
4366 rc = VDInterfaceAdd(&pCache->VDIo.VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
4367 &pDisk->VDIIOIntCallbacks, &pCache->VDIo, &pCache->pVDIfsCache);
4368 AssertRC(rc);
4369
4370 pCache->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
4371 rc = pCache->Backend->pfnOpen(pCache->pszFilename,
4372 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
4373 pDisk->pVDIfsDisk,
4374 pCache->pVDIfsCache,
4375 &pCache->pBackendData);
4376 /* If the open in read-write mode failed, retry in read-only mode. */
4377 if (RT_FAILURE(rc))
4378 {
4379 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
4380 && ( rc == VERR_ACCESS_DENIED
4381 || rc == VERR_PERMISSION_DENIED
4382 || rc == VERR_WRITE_PROTECT
4383 || rc == VERR_SHARING_VIOLATION
4384 || rc == VERR_FILE_LOCK_FAILED))
4385 rc = pCache->Backend->pfnOpen(pCache->pszFilename,
4386 (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
4387 | VD_OPEN_FLAGS_READONLY,
4388 pDisk->pVDIfsDisk,
4389 pCache->pVDIfsCache,
4390 &pCache->pBackendData);
4391 if (RT_FAILURE(rc))
4392 {
4393 rc = vdError(pDisk, rc, RT_SRC_POS,
4394 N_("VD: error %Rrc opening image file '%s'"), rc, pszFilename);
4395 break;
4396 }
4397 }
4398
4399 /* Lock disk for writing, as we modify pDisk information below. */
4400 rc2 = vdThreadStartWrite(pDisk);
4401 AssertRC(rc2);
4402 fLockWrite = true;
4403
4404 /*
4405 * Check that the modification UUID of the cache and last image
4406 * match. If not the image was modified in-between without the cache.
4407 * The cache might contain stale data.
4408 */
4409 RTUUID UuidImage, UuidCache;
4410
4411 rc = pCache->Backend->pfnGetModificationUuid(pCache->pBackendData,
4412 &UuidCache);
4413 if (RT_SUCCESS(rc))
4414 {
4415 rc = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
4416 &UuidImage);
4417 if (RT_SUCCESS(rc))
4418 {
4419 if (RTUuidCompare(&UuidImage, &UuidCache))
4420 rc = VERR_VD_CACHE_NOT_UP_TO_DATE;
4421 }
4422 }
4423
4424 /*
4425 * We assume that the user knows what he is doing if one of the images
4426 * doesn't support the modification uuid.
4427 */
4428 if (rc == VERR_NOT_SUPPORTED)
4429 rc = VINF_SUCCESS;
4430
4431 if (RT_SUCCESS(rc))
4432 {
4433 /* Cache successfully opened, make it the current one. */
4434 if (!pDisk->pCache)
4435 pDisk->pCache = pCache;
4436 else
4437 rc = VERR_VD_CACHE_ALREADY_EXISTS;
4438 }
4439
4440 if (RT_FAILURE(rc))
4441 {
4442 /* Error detected, but image opened. Close image. */
4443 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, false);
4444 AssertRC(rc2);
4445 pCache->pBackendData = NULL;
4446 }
4447 } while (0);
4448
4449 if (RT_UNLIKELY(fLockWrite))
4450 {
4451 rc2 = vdThreadFinishWrite(pDisk);
4452 AssertRC(rc2);
4453 }
4454
4455 if (RT_FAILURE(rc))
4456 {
4457 if (pCache)
4458 {
4459 if (pCache->pszFilename)
4460 RTStrFree(pCache->pszFilename);
4461 RTMemFree(pCache);
4462 }
4463 }
4464
4465 LogFlowFunc(("returns %Rrc\n", rc));
4466 return rc;
4467}
4468
4469/**
4470 * Creates and opens a new base image file.
4471 *
4472 * @returns VBox status code.
4473 * @param pDisk Pointer to HDD container.
4474 * @param pszBackend Name of the image file backend to use.
4475 * @param pszFilename Name of the image file to create.
4476 * @param cbSize Image size in bytes.
4477 * @param uImageFlags Flags specifying special image features.
4478 * @param pszComment Pointer to image comment. NULL is ok.
4479 * @param pPCHSGeometry Pointer to physical disk geometry <= (16383,16,63). Not NULL.
4480 * @param pLCHSGeometry Pointer to logical disk geometry <= (x,255,63). Not NULL.
4481 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
4482 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
4483 * @param pVDIfsImage Pointer to the per-image VD interface list.
4484 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
4485 */
4486VBOXDDU_DECL(int) VDCreateBase(PVBOXHDD pDisk, const char *pszBackend,
4487 const char *pszFilename, uint64_t cbSize,
4488 unsigned uImageFlags, const char *pszComment,
4489 PCVDGEOMETRY pPCHSGeometry,
4490 PCVDGEOMETRY pLCHSGeometry,
4491 PCRTUUID pUuid, unsigned uOpenFlags,
4492 PVDINTERFACE pVDIfsImage,
4493 PVDINTERFACE pVDIfsOperation)
4494{
4495 int rc = VINF_SUCCESS;
4496 int rc2;
4497 bool fLockWrite = false, fLockRead = false;
4498 PVDIMAGE pImage = NULL;
4499 RTUUID uuid;
4500
4501 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",
4502 pDisk, pszBackend, pszFilename, cbSize, uImageFlags, pszComment,
4503 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
4504 pPCHSGeometry->cSectors, pLCHSGeometry->cCylinders,
4505 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors, pUuid,
4506 uOpenFlags, pVDIfsImage, pVDIfsOperation));
4507
4508 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
4509 VDINTERFACETYPE_PROGRESS);
4510 PVDINTERFACEPROGRESS pCbProgress = NULL;
4511 if (pIfProgress)
4512 pCbProgress = VDGetInterfaceProgress(pIfProgress);
4513
4514 do
4515 {
4516 /* sanity check */
4517 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4518 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4519
4520 /* Check arguments. */
4521 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
4522 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
4523 rc = VERR_INVALID_PARAMETER);
4524 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
4525 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
4526 rc = VERR_INVALID_PARAMETER);
4527 AssertMsgBreakStmt(cbSize,
4528 ("cbSize=%llu\n", cbSize),
4529 rc = VERR_INVALID_PARAMETER);
4530 AssertMsgBreakStmt( ((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0)
4531 || ((uImageFlags & (VD_IMAGE_FLAGS_FIXED | VD_IMAGE_FLAGS_DIFF)) != VD_IMAGE_FLAGS_FIXED),
4532 ("uImageFlags=%#x\n", uImageFlags),
4533 rc = VERR_INVALID_PARAMETER);
4534 /* The PCHS geometry fields may be 0 to leave it for later. */
4535 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
4536 && pPCHSGeometry->cHeads <= 16
4537 && pPCHSGeometry->cSectors <= 63,
4538 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
4539 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
4540 pPCHSGeometry->cSectors),
4541 rc = VERR_INVALID_PARAMETER);
4542 /* The LCHS geometry fields may be 0 to leave it to later autodetection. */
4543 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
4544 && pLCHSGeometry->cHeads <= 255
4545 && pLCHSGeometry->cSectors <= 63,
4546 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
4547 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
4548 pLCHSGeometry->cSectors),
4549 rc = VERR_INVALID_PARAMETER);
4550 /* The UUID may be NULL. */
4551 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
4552 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
4553 rc = VERR_INVALID_PARAMETER);
4554 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
4555 ("uOpenFlags=%#x\n", uOpenFlags),
4556 rc = VERR_INVALID_PARAMETER);
4557
4558 /* Check state. Needs a temporary read lock. Holding the write lock
4559 * all the time would be blocking other activities for too long. */
4560 rc2 = vdThreadStartRead(pDisk);
4561 AssertRC(rc2);
4562 fLockRead = true;
4563 AssertMsgBreakStmt(pDisk->cImages == 0,
4564 ("Create base image cannot be done with other images open\n"),
4565 rc = VERR_VD_INVALID_STATE);
4566 rc2 = vdThreadFinishRead(pDisk);
4567 AssertRC(rc2);
4568 fLockRead = false;
4569
4570 /* Set up image descriptor. */
4571 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
4572 if (!pImage)
4573 {
4574 rc = VERR_NO_MEMORY;
4575 break;
4576 }
4577 pImage->pszFilename = RTStrDup(pszFilename);
4578 if (!pImage->pszFilename)
4579 {
4580 rc = VERR_NO_MEMORY;
4581 break;
4582 }
4583 pImage->VDIo.pDisk = pDisk;
4584 pImage->pVDIfsImage = pVDIfsImage;
4585
4586 /* Set up the I/O interface. */
4587 pImage->VDIo.pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
4588 if (pImage->VDIo.pInterfaceIO)
4589 pImage->VDIo.pInterfaceIOCallbacks = VDGetInterfaceIO(pImage->VDIo.pInterfaceIO);
4590 else
4591 {
4592 rc = VDInterfaceAdd(&pImage->VDIo.VDIIO, "VD_IO", VDINTERFACETYPE_IO,
4593 &pDisk->VDIIOCallbacks, pDisk, &pVDIfsImage);
4594 pImage->VDIo.pInterfaceIO = &pImage->VDIo.VDIIO;
4595 pImage->VDIo.pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
4596 }
4597
4598 /* Set up the internal I/O interface. */
4599 AssertBreakStmt(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
4600 rc = VERR_INVALID_PARAMETER);
4601 rc = VDInterfaceAdd(&pImage->VDIo.VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
4602 &pDisk->VDIIOIntCallbacks, &pImage->VDIo, &pImage->pVDIfsImage);
4603 AssertRC(rc);
4604
4605 rc = vdFindBackend(pszBackend, &pImage->Backend);
4606 if (RT_FAILURE(rc))
4607 break;
4608 if (!pImage->Backend)
4609 {
4610 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4611 N_("VD: unknown backend name '%s'"), pszBackend);
4612 break;
4613 }
4614 if (!(pImage->Backend->uBackendCaps & ( VD_CAP_CREATE_FIXED
4615 | VD_CAP_CREATE_DYNAMIC)))
4616 {
4617 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4618 N_("VD: backend '%s' cannot create base images"), pszBackend);
4619 break;
4620 }
4621
4622 /* Create UUID if the caller didn't specify one. */
4623 if (!pUuid)
4624 {
4625 rc = RTUuidCreate(&uuid);
4626 if (RT_FAILURE(rc))
4627 {
4628 rc = vdError(pDisk, rc, RT_SRC_POS,
4629 N_("VD: cannot generate UUID for image '%s'"),
4630 pszFilename);
4631 break;
4632 }
4633 pUuid = &uuid;
4634 }
4635
4636 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
4637 uImageFlags &= ~VD_IMAGE_FLAGS_DIFF;
4638 rc = pImage->Backend->pfnCreate(pImage->pszFilename, cbSize,
4639 uImageFlags, pszComment, pPCHSGeometry,
4640 pLCHSGeometry, pUuid,
4641 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
4642 0, 99,
4643 pDisk->pVDIfsDisk,
4644 pImage->pVDIfsImage,
4645 pVDIfsOperation,
4646 &pImage->pBackendData);
4647
4648 if (RT_SUCCESS(rc))
4649 {
4650 pImage->VDIo.pBackendData = pImage->pBackendData;
4651 pImage->uImageFlags = uImageFlags;
4652
4653 /* Force sane optimization settings. It's not worth avoiding writes
4654 * to fixed size images. The overhead would have almost no payback. */
4655 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
4656 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
4657
4658 /* Lock disk for writing, as we modify pDisk information below. */
4659 rc2 = vdThreadStartWrite(pDisk);
4660 AssertRC(rc2);
4661 fLockWrite = true;
4662
4663 /** @todo optionally check UUIDs */
4664
4665 /* Re-check state, as the lock wasn't held and another image
4666 * creation call could have been done by another thread. */
4667 AssertMsgStmt(pDisk->cImages == 0,
4668 ("Create base image cannot be done with other images open\n"),
4669 rc = VERR_VD_INVALID_STATE);
4670 }
4671
4672 if (RT_SUCCESS(rc))
4673 {
4674 /* Cache disk information. */
4675 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
4676
4677 /* Cache PCHS geometry. */
4678 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
4679 &pDisk->PCHSGeometry);
4680 if (RT_FAILURE(rc2))
4681 {
4682 pDisk->PCHSGeometry.cCylinders = 0;
4683 pDisk->PCHSGeometry.cHeads = 0;
4684 pDisk->PCHSGeometry.cSectors = 0;
4685 }
4686 else
4687 {
4688 /* Make sure the CHS geometry is properly clipped. */
4689 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
4690 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
4691 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
4692 }
4693
4694 /* Cache LCHS geometry. */
4695 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
4696 &pDisk->LCHSGeometry);
4697 if (RT_FAILURE(rc2))
4698 {
4699 pDisk->LCHSGeometry.cCylinders = 0;
4700 pDisk->LCHSGeometry.cHeads = 0;
4701 pDisk->LCHSGeometry.cSectors = 0;
4702 }
4703 else
4704 {
4705 /* Make sure the CHS geometry is properly clipped. */
4706 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
4707 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
4708 }
4709
4710 /* Image successfully opened, make it the last image. */
4711 vdAddImageToList(pDisk, pImage);
4712 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
4713 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
4714 }
4715 else
4716 {
4717 /* Error detected, image may or may not be opened. Close and delete
4718 * image if it was opened. */
4719 if (pImage->pBackendData)
4720 {
4721 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, true);
4722 AssertRC(rc2);
4723 pImage->pBackendData = NULL;
4724 }
4725 }
4726 } while (0);
4727
4728 if (RT_UNLIKELY(fLockWrite))
4729 {
4730 rc2 = vdThreadFinishWrite(pDisk);
4731 AssertRC(rc2);
4732 }
4733 else if (RT_UNLIKELY(fLockRead))
4734 {
4735 rc2 = vdThreadFinishRead(pDisk);
4736 AssertRC(rc2);
4737 }
4738
4739 if (RT_FAILURE(rc))
4740 {
4741 if (pImage)
4742 {
4743 if (pImage->pszFilename)
4744 RTStrFree(pImage->pszFilename);
4745 RTMemFree(pImage);
4746 }
4747 }
4748
4749 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
4750 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
4751
4752 LogFlowFunc(("returns %Rrc\n", rc));
4753 return rc;
4754}
4755
4756/**
4757 * Creates and opens a new differencing image file in HDD container.
4758 * See comments for VDOpen function about differencing images.
4759 *
4760 * @returns VBox status code.
4761 * @param pDisk Pointer to HDD container.
4762 * @param pszBackend Name of the image file backend to use.
4763 * @param pszFilename Name of the differencing image file to create.
4764 * @param uImageFlags Flags specifying special image features.
4765 * @param pszComment Pointer to image comment. NULL is ok.
4766 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
4767 * @param pParentUuid New parent UUID of the image. If NULL, the UUID is queried automatically.
4768 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
4769 * @param pVDIfsImage Pointer to the per-image VD interface list.
4770 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
4771 */
4772VBOXDDU_DECL(int) VDCreateDiff(PVBOXHDD pDisk, const char *pszBackend,
4773 const char *pszFilename, unsigned uImageFlags,
4774 const char *pszComment, PCRTUUID pUuid,
4775 PCRTUUID pParentUuid, unsigned uOpenFlags,
4776 PVDINTERFACE pVDIfsImage,
4777 PVDINTERFACE pVDIfsOperation)
4778{
4779 int rc = VINF_SUCCESS;
4780 int rc2;
4781 bool fLockWrite = false, fLockRead = false;
4782 PVDIMAGE pImage = NULL;
4783 RTUUID uuid;
4784
4785 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
4786 pDisk, pszBackend, pszFilename, uImageFlags, pszComment, pUuid, uOpenFlags, pVDIfsImage, pVDIfsOperation));
4787
4788 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
4789 VDINTERFACETYPE_PROGRESS);
4790 PVDINTERFACEPROGRESS pCbProgress = NULL;
4791 if (pIfProgress)
4792 pCbProgress = VDGetInterfaceProgress(pIfProgress);
4793
4794 do
4795 {
4796 /* sanity check */
4797 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4798 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4799
4800 /* Check arguments. */
4801 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
4802 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
4803 rc = VERR_INVALID_PARAMETER);
4804 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
4805 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
4806 rc = VERR_INVALID_PARAMETER);
4807 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
4808 ("uImageFlags=%#x\n", uImageFlags),
4809 rc = VERR_INVALID_PARAMETER);
4810 /* The UUID may be NULL. */
4811 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
4812 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
4813 rc = VERR_INVALID_PARAMETER);
4814 /* The parent UUID may be NULL. */
4815 AssertMsgBreakStmt(pParentUuid == NULL || VALID_PTR(pParentUuid),
4816 ("pParentUuid=%#p ParentUUID=%RTuuid\n", pParentUuid, pParentUuid),
4817 rc = VERR_INVALID_PARAMETER);
4818 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
4819 ("uOpenFlags=%#x\n", uOpenFlags),
4820 rc = VERR_INVALID_PARAMETER);
4821
4822 /* Check state. Needs a temporary read lock. Holding the write lock
4823 * all the time would be blocking other activities for too long. */
4824 rc2 = vdThreadStartRead(pDisk);
4825 AssertRC(rc2);
4826 fLockRead = true;
4827 AssertMsgBreakStmt(pDisk->cImages != 0,
4828 ("Create diff image cannot be done without other images open\n"),
4829 rc = VERR_VD_INVALID_STATE);
4830 rc2 = vdThreadFinishRead(pDisk);
4831 AssertRC(rc2);
4832 fLockRead = false;
4833
4834 /* Set up image descriptor. */
4835 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
4836 if (!pImage)
4837 {
4838 rc = VERR_NO_MEMORY;
4839 break;
4840 }
4841 pImage->pszFilename = RTStrDup(pszFilename);
4842 if (!pImage->pszFilename)
4843 {
4844 rc = VERR_NO_MEMORY;
4845 break;
4846 }
4847
4848 rc = vdFindBackend(pszBackend, &pImage->Backend);
4849 if (RT_FAILURE(rc))
4850 break;
4851 if (!pImage->Backend)
4852 {
4853 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4854 N_("VD: unknown backend name '%s'"), pszBackend);
4855 break;
4856 }
4857 if ( !(pImage->Backend->uBackendCaps & VD_CAP_DIFF)
4858 || !(pImage->Backend->uBackendCaps & ( VD_CAP_CREATE_FIXED
4859 | VD_CAP_CREATE_DYNAMIC)))
4860 {
4861 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4862 N_("VD: backend '%s' cannot create diff images"), pszBackend);
4863 break;
4864 }
4865
4866 pImage->VDIo.pDisk = pDisk;
4867 pImage->pVDIfsImage = pVDIfsImage;
4868
4869 /* Set up the I/O interface. */
4870 pImage->VDIo.pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
4871 if (pImage->VDIo.pInterfaceIO)
4872 pImage->VDIo.pInterfaceIOCallbacks = VDGetInterfaceIO(pImage->VDIo.pInterfaceIO);
4873 else
4874 {
4875 rc = VDInterfaceAdd(&pImage->VDIo.VDIIO, "VD_IO", VDINTERFACETYPE_IO,
4876 &pDisk->VDIIOCallbacks, pDisk, &pVDIfsImage);
4877 pImage->VDIo.pInterfaceIO = &pImage->VDIo.VDIIO;
4878 pImage->VDIo.pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
4879 }
4880
4881 /* Set up the internal I/O interface. */
4882 AssertBreakStmt(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
4883 rc = VERR_INVALID_PARAMETER);
4884 rc = VDInterfaceAdd(&pImage->VDIo.VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
4885 &pDisk->VDIIOIntCallbacks, &pImage->VDIo, &pImage->pVDIfsImage);
4886 AssertRC(rc);
4887
4888 /* Create UUID if the caller didn't specify one. */
4889 if (!pUuid)
4890 {
4891 rc = RTUuidCreate(&uuid);
4892 if (RT_FAILURE(rc))
4893 {
4894 rc = vdError(pDisk, rc, RT_SRC_POS,
4895 N_("VD: cannot generate UUID for image '%s'"),
4896 pszFilename);
4897 break;
4898 }
4899 pUuid = &uuid;
4900 }
4901
4902 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
4903 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
4904 rc = pImage->Backend->pfnCreate(pImage->pszFilename, pDisk->cbSize,
4905 uImageFlags | VD_IMAGE_FLAGS_DIFF,
4906 pszComment, &pDisk->PCHSGeometry,
4907 &pDisk->LCHSGeometry, pUuid,
4908 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
4909 0, 99,
4910 pDisk->pVDIfsDisk,
4911 pImage->pVDIfsImage,
4912 pVDIfsOperation,
4913 &pImage->pBackendData);
4914
4915 if (RT_SUCCESS(rc))
4916 {
4917 pImage->VDIo.pBackendData = pImage->pBackendData;
4918 pImage->uImageFlags = uImageFlags;
4919
4920 /* Lock disk for writing, as we modify pDisk information below. */
4921 rc2 = vdThreadStartWrite(pDisk);
4922 AssertRC(rc2);
4923 fLockWrite = true;
4924
4925 /* Switch previous image to read-only mode. */
4926 unsigned uOpenFlagsPrevImg;
4927 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
4928 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
4929 {
4930 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
4931 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pBackendData, uOpenFlagsPrevImg);
4932 }
4933
4934 /** @todo optionally check UUIDs */
4935
4936 /* Re-check state, as the lock wasn't held and another image
4937 * creation call could have been done by another thread. */
4938 AssertMsgStmt(pDisk->cImages != 0,
4939 ("Create diff image cannot be done without other images open\n"),
4940 rc = VERR_VD_INVALID_STATE);
4941 }
4942
4943 if (RT_SUCCESS(rc))
4944 {
4945 RTUUID Uuid;
4946 RTTIMESPEC ts;
4947
4948 if (pParentUuid && !RTUuidIsNull(pParentUuid))
4949 {
4950 Uuid = *pParentUuid;
4951 pImage->Backend->pfnSetParentUuid(pImage->pBackendData, &Uuid);
4952 }
4953 else
4954 {
4955 rc2 = pDisk->pLast->Backend->pfnGetUuid(pDisk->pLast->pBackendData,
4956 &Uuid);
4957 if (RT_SUCCESS(rc2))
4958 pImage->Backend->pfnSetParentUuid(pImage->pBackendData, &Uuid);
4959 }
4960 rc2 = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
4961 &Uuid);
4962 if (RT_SUCCESS(rc2))
4963 pImage->Backend->pfnSetParentModificationUuid(pImage->pBackendData,
4964 &Uuid);
4965 if (pDisk->pLast->Backend->pfnGetTimeStamp)
4966 rc2 = pDisk->pLast->Backend->pfnGetTimeStamp(pDisk->pLast->pBackendData,
4967 &ts);
4968 else
4969 rc2 = VERR_NOT_IMPLEMENTED;
4970 if (RT_SUCCESS(rc2) && pImage->Backend->pfnSetParentTimeStamp)
4971 pImage->Backend->pfnSetParentTimeStamp(pImage->pBackendData, &ts);
4972
4973 if (pImage->Backend->pfnSetParentFilename)
4974 rc2 = pImage->Backend->pfnSetParentFilename(pImage->pBackendData, pDisk->pLast->pszFilename);
4975 }
4976
4977 if (RT_SUCCESS(rc))
4978 {
4979 /* Image successfully opened, make it the last image. */
4980 vdAddImageToList(pDisk, pImage);
4981 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
4982 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
4983 }
4984 else
4985 {
4986 /* Error detected, but image opened. Close and delete image. */
4987 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, true);
4988 AssertRC(rc2);
4989 pImage->pBackendData = NULL;
4990 }
4991 } while (0);
4992
4993 if (RT_UNLIKELY(fLockWrite))
4994 {
4995 rc2 = vdThreadFinishWrite(pDisk);
4996 AssertRC(rc2);
4997 }
4998 else if (RT_UNLIKELY(fLockRead))
4999 {
5000 rc2 = vdThreadFinishRead(pDisk);
5001 AssertRC(rc2);
5002 }
5003
5004 if (RT_FAILURE(rc))
5005 {
5006 if (pImage)
5007 {
5008 if (pImage->pszFilename)
5009 RTStrFree(pImage->pszFilename);
5010 RTMemFree(pImage);
5011 }
5012 }
5013
5014 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
5015 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
5016
5017 LogFlowFunc(("returns %Rrc\n", rc));
5018 return rc;
5019}
5020
5021
5022/**
5023 * Creates and opens new cache image file in HDD container.
5024 *
5025 * @return VBox status code.
5026 * @param pDisk Name of the cache file backend to use (case insensitive).
5027 * @param pszFilename Name of the differencing cache file to create.
5028 * @param cbSize Maximum size of the cache.
5029 * @param uImageFlags Flags specifying special cache features.
5030 * @param pszComment Pointer to image comment. NULL is ok.
5031 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
5032 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
5033 * @param pVDIfsCache Pointer to the per-cache VD interface list.
5034 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
5035 */
5036VBOXDDU_DECL(int) VDCreateCache(PVBOXHDD pDisk, const char *pszBackend,
5037 const char *pszFilename, uint64_t cbSize,
5038 unsigned uImageFlags, const char *pszComment,
5039 PCRTUUID pUuid, unsigned uOpenFlags,
5040 PVDINTERFACE pVDIfsCache, PVDINTERFACE pVDIfsOperation)
5041{
5042 int rc = VINF_SUCCESS;
5043 int rc2;
5044 bool fLockWrite = false, fLockRead = false;
5045 PVDCACHE pCache = NULL;
5046 RTUUID uuid;
5047
5048 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
5049 pDisk, pszBackend, pszFilename, cbSize, uImageFlags, pszComment, pUuid, uOpenFlags, pVDIfsCache, pVDIfsOperation));
5050
5051 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
5052 VDINTERFACETYPE_PROGRESS);
5053 PVDINTERFACEPROGRESS pCbProgress = NULL;
5054 if (pIfProgress)
5055 pCbProgress = VDGetInterfaceProgress(pIfProgress);
5056
5057 do
5058 {
5059 /* sanity check */
5060 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
5061 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5062
5063 /* Check arguments. */
5064 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
5065 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
5066 rc = VERR_INVALID_PARAMETER);
5067 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
5068 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
5069 rc = VERR_INVALID_PARAMETER);
5070 AssertMsgBreakStmt(cbSize,
5071 ("cbSize=%llu\n", cbSize),
5072 rc = VERR_INVALID_PARAMETER);
5073 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
5074 ("uImageFlags=%#x\n", uImageFlags),
5075 rc = VERR_INVALID_PARAMETER);
5076 /* The UUID may be NULL. */
5077 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
5078 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
5079 rc = VERR_INVALID_PARAMETER);
5080 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
5081 ("uOpenFlags=%#x\n", uOpenFlags),
5082 rc = VERR_INVALID_PARAMETER);
5083
5084 /* Check state. Needs a temporary read lock. Holding the write lock
5085 * all the time would be blocking other activities for too long. */
5086 rc2 = vdThreadStartRead(pDisk);
5087 AssertRC(rc2);
5088 fLockRead = true;
5089 AssertMsgBreakStmt(!pDisk->pCache,
5090 ("Create cache image cannot be done with a cache already attached\n"),
5091 rc = VERR_VD_CACHE_ALREADY_EXISTS);
5092 rc2 = vdThreadFinishRead(pDisk);
5093 AssertRC(rc2);
5094 fLockRead = false;
5095
5096 /* Set up image descriptor. */
5097 pCache = (PVDCACHE)RTMemAllocZ(sizeof(VDCACHE));
5098 if (!pCache)
5099 {
5100 rc = VERR_NO_MEMORY;
5101 break;
5102 }
5103 pCache->pszFilename = RTStrDup(pszFilename);
5104 if (!pCache->pszFilename)
5105 {
5106 rc = VERR_NO_MEMORY;
5107 break;
5108 }
5109
5110 rc = vdFindCacheBackend(pszBackend, &pCache->Backend);
5111 if (RT_FAILURE(rc))
5112 break;
5113 if (!pCache->Backend)
5114 {
5115 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
5116 N_("VD: unknown backend name '%s'"), pszBackend);
5117 break;
5118 }
5119
5120 pCache->VDIo.pDisk = pDisk;
5121 pCache->pVDIfsCache = pVDIfsCache;
5122
5123 /* Set up the I/O interface. */
5124 pCache->VDIo.pInterfaceIO = VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IO);
5125 if (pCache->VDIo.pInterfaceIO)
5126 pCache->VDIo.pInterfaceIOCallbacks = VDGetInterfaceIO(pCache->VDIo.pInterfaceIO);
5127 else
5128 {
5129 rc = VDInterfaceAdd(&pCache->VDIo.VDIIO, "VD_IO", VDINTERFACETYPE_IO,
5130 &pDisk->VDIIOCallbacks, pDisk, &pVDIfsCache);
5131 pCache->VDIo.pInterfaceIO = &pCache->VDIo.VDIIO;
5132 pCache->VDIo.pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
5133 }
5134
5135 /* Set up the internal I/O interface. */
5136 AssertBreakStmt(!VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IOINT),
5137 rc = VERR_INVALID_PARAMETER);
5138 rc = VDInterfaceAdd(&pCache->VDIo.VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
5139 &pDisk->VDIIOIntCallbacks, &pCache->VDIo, &pCache->pVDIfsCache);
5140 AssertRC(rc);
5141
5142 /* Create UUID if the caller didn't specify one. */
5143 if (!pUuid)
5144 {
5145 rc = RTUuidCreate(&uuid);
5146 if (RT_FAILURE(rc))
5147 {
5148 rc = vdError(pDisk, rc, RT_SRC_POS,
5149 N_("VD: cannot generate UUID for image '%s'"),
5150 pszFilename);
5151 break;
5152 }
5153 pUuid = &uuid;
5154 }
5155
5156 pCache->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
5157 rc = pCache->Backend->pfnCreate(pCache->pszFilename, cbSize,
5158 uImageFlags,
5159 pszComment, pUuid,
5160 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
5161 0, 99,
5162 pDisk->pVDIfsDisk,
5163 pCache->pVDIfsCache,
5164 pVDIfsOperation,
5165 &pCache->pBackendData);
5166
5167 if (RT_SUCCESS(rc))
5168 {
5169 /* Lock disk for writing, as we modify pDisk information below. */
5170 rc2 = vdThreadStartWrite(pDisk);
5171 AssertRC(rc2);
5172 fLockWrite = true;
5173
5174 pCache->VDIo.pBackendData = pCache->pBackendData;
5175
5176 /* Re-check state, as the lock wasn't held and another image
5177 * creation call could have been done by another thread. */
5178 AssertMsgStmt(!pDisk->pCache,
5179 ("Create cache image cannot be done with another cache open\n"),
5180 rc = VERR_VD_CACHE_ALREADY_EXISTS);
5181 }
5182
5183 if ( RT_SUCCESS(rc)
5184 && pDisk->pLast)
5185 {
5186 RTUUID UuidModification;
5187
5188 /* Set same modification Uuid as the last image. */
5189 rc = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
5190 &UuidModification);
5191 if (RT_SUCCESS(rc))
5192 {
5193 rc = pCache->Backend->pfnSetModificationUuid(pCache->pBackendData,
5194 &UuidModification);
5195 }
5196
5197 if (rc == VERR_NOT_SUPPORTED)
5198 rc = VINF_SUCCESS;
5199 }
5200
5201 if (RT_SUCCESS(rc))
5202 {
5203 /* Cache successfully created. */
5204 pDisk->pCache = pCache;
5205 }
5206 else
5207 {
5208 /* Error detected, but image opened. Close and delete image. */
5209 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, true);
5210 AssertRC(rc2);
5211 pCache->pBackendData = NULL;
5212 }
5213 } while (0);
5214
5215 if (RT_UNLIKELY(fLockWrite))
5216 {
5217 rc2 = vdThreadFinishWrite(pDisk);
5218 AssertRC(rc2);
5219 }
5220 else if (RT_UNLIKELY(fLockRead))
5221 {
5222 rc2 = vdThreadFinishRead(pDisk);
5223 AssertRC(rc2);
5224 }
5225
5226 if (RT_FAILURE(rc))
5227 {
5228 if (pCache)
5229 {
5230 if (pCache->pszFilename)
5231 RTStrFree(pCache->pszFilename);
5232 RTMemFree(pCache);
5233 }
5234 }
5235
5236 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
5237 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
5238
5239 LogFlowFunc(("returns %Rrc\n", rc));
5240 return rc;
5241}
5242
5243/**
5244 * Merges two images (not necessarily with direct parent/child relationship).
5245 * As a side effect the source image and potentially the other images which
5246 * are also merged to the destination are deleted from both the disk and the
5247 * images in the HDD container.
5248 *
5249 * @returns VBox status code.
5250 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
5251 * @param pDisk Pointer to HDD container.
5252 * @param nImageFrom Name of the image file to merge from.
5253 * @param nImageTo Name of the image file to merge to.
5254 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
5255 */
5256VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
5257 unsigned nImageTo, PVDINTERFACE pVDIfsOperation)
5258{
5259 int rc = VINF_SUCCESS;
5260 int rc2;
5261 bool fLockWrite = false, fLockRead = false;
5262 void *pvBuf = NULL;
5263
5264 LogFlowFunc(("pDisk=%#p nImageFrom=%u nImageTo=%u pVDIfsOperation=%#p\n",
5265 pDisk, nImageFrom, nImageTo, pVDIfsOperation));
5266
5267 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
5268 VDINTERFACETYPE_PROGRESS);
5269 PVDINTERFACEPROGRESS pCbProgress = NULL;
5270 if (pIfProgress)
5271 pCbProgress = VDGetInterfaceProgress(pIfProgress);
5272
5273 do
5274 {
5275 /* sanity check */
5276 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
5277 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5278
5279 /* For simplicity reasons lock for writing as the image reopen below
5280 * might need it. After all the reopen is usually needed. */
5281 rc2 = vdThreadStartWrite(pDisk);
5282 AssertRC(rc2);
5283 fLockWrite = true;
5284 PVDIMAGE pImageFrom = vdGetImageByNumber(pDisk, nImageFrom);
5285 PVDIMAGE pImageTo = vdGetImageByNumber(pDisk, nImageTo);
5286 if (!pImageFrom || !pImageTo)
5287 {
5288 rc = VERR_VD_IMAGE_NOT_FOUND;
5289 break;
5290 }
5291 AssertBreakStmt(pImageFrom != pImageTo, rc = VERR_INVALID_PARAMETER);
5292
5293 /* Make sure destination image is writable. */
5294 unsigned uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pBackendData);
5295 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
5296 {
5297 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
5298 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
5299 uOpenFlags);
5300 if (RT_FAILURE(rc))
5301 break;
5302 }
5303
5304 /* Get size of destination image. */
5305 uint64_t cbSize = pImageTo->Backend->pfnGetSize(pImageTo->pBackendData);
5306 rc2 = vdThreadFinishWrite(pDisk);
5307 AssertRC(rc2);
5308 fLockWrite = false;
5309
5310 /* Allocate tmp buffer. */
5311 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
5312 if (!pvBuf)
5313 {
5314 rc = VERR_NO_MEMORY;
5315 break;
5316 }
5317
5318 /* Merging is done directly on the images itself. This potentially
5319 * causes trouble if the disk is full in the middle of operation. */
5320 if (nImageFrom < nImageTo)
5321 {
5322 /* Merge parent state into child. This means writing all not
5323 * allocated blocks in the destination image which are allocated in
5324 * the images to be merged. */
5325 uint64_t uOffset = 0;
5326 uint64_t cbRemaining = cbSize;
5327 do
5328 {
5329 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
5330
5331 /* Need to hold the write lock during a read-write operation. */
5332 rc2 = vdThreadStartWrite(pDisk);
5333 AssertRC(rc2);
5334 fLockWrite = true;
5335
5336 rc = pImageTo->Backend->pfnRead(pImageTo->pBackendData,
5337 uOffset, pvBuf, cbThisRead,
5338 &cbThisRead);
5339 if (rc == VERR_VD_BLOCK_FREE)
5340 {
5341 /* Search for image with allocated block. Do not attempt to
5342 * read more than the previous reads marked as valid.
5343 * Otherwise this would return stale data when different
5344 * block sizes are used for the images. */
5345 for (PVDIMAGE pCurrImage = pImageTo->pPrev;
5346 pCurrImage != NULL && pCurrImage != pImageFrom->pPrev && rc == VERR_VD_BLOCK_FREE;
5347 pCurrImage = pCurrImage->pPrev)
5348 {
5349 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
5350 uOffset, pvBuf,
5351 cbThisRead,
5352 &cbThisRead);
5353 }
5354
5355 if (rc != VERR_VD_BLOCK_FREE)
5356 {
5357 if (RT_FAILURE(rc))
5358 break;
5359 /* Updating the cache is required because this might be a live merge. */
5360 rc = vdWriteHelper(pDisk, pImageTo, pImageFrom->pPrev,
5361 uOffset, pvBuf, cbThisRead,
5362 true /* fUpdateCache */);
5363 if (RT_FAILURE(rc))
5364 break;
5365 }
5366 else
5367 rc = VINF_SUCCESS;
5368 }
5369 else if (RT_FAILURE(rc))
5370 break;
5371
5372 rc2 = vdThreadFinishWrite(pDisk);
5373 AssertRC(rc2);
5374 fLockWrite = false;
5375
5376 uOffset += cbThisRead;
5377 cbRemaining -= cbThisRead;
5378
5379 if (pCbProgress && pCbProgress->pfnProgress)
5380 {
5381 /** @todo r=klaus: this can update the progress to the same
5382 * percentage over and over again if the image format makes
5383 * relatively small increments. */
5384 rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
5385 uOffset * 99 / cbSize);
5386 if (RT_FAILURE(rc))
5387 break;
5388 }
5389 } while (uOffset < cbSize);
5390 }
5391 else
5392 {
5393 /*
5394 * We may need to update the parent uuid of the child coming after
5395 * the last image to be merged. We have to reopen it read/write.
5396 *
5397 * This is done before we do the actual merge to prevent an
5398 * inconsistent chain if the mode change fails for some reason.
5399 */
5400 if (pImageFrom->pNext)
5401 {
5402 PVDIMAGE pImageChild = pImageFrom->pNext;
5403
5404 /* Take the write lock. */
5405 rc2 = vdThreadStartWrite(pDisk);
5406 AssertRC(rc2);
5407 fLockWrite = true;
5408
5409 /* We need to open the image in read/write mode. */
5410 uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pBackendData);
5411
5412 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
5413 {
5414 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
5415 rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pBackendData,
5416 uOpenFlags);
5417 if (RT_FAILURE(rc))
5418 break;
5419 }
5420
5421 rc2 = vdThreadFinishWrite(pDisk);
5422 AssertRC(rc2);
5423 fLockWrite = false;
5424 }
5425
5426 /* If the merge is from the last image we have to relay all writes
5427 * to the merge destination as well, so that concurrent writes
5428 * (in case of a live merge) are handled correctly. */
5429 if (!pImageFrom->pNext)
5430 {
5431 /* Take the write lock. */
5432 rc2 = vdThreadStartWrite(pDisk);
5433 AssertRC(rc2);
5434 fLockWrite = true;
5435
5436 pDisk->pImageRelay = pImageTo;
5437
5438 rc2 = vdThreadFinishWrite(pDisk);
5439 AssertRC(rc2);
5440 fLockWrite = false;
5441 }
5442
5443 /* Merge child state into parent. This means writing all blocks
5444 * which are allocated in the image up to the source image to the
5445 * destination image. */
5446 uint64_t uOffset = 0;
5447 uint64_t cbRemaining = cbSize;
5448 do
5449 {
5450 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
5451 rc = VERR_VD_BLOCK_FREE;
5452
5453 /* Need to hold the write lock during a read-write operation. */
5454 rc2 = vdThreadStartWrite(pDisk);
5455 AssertRC(rc2);
5456 fLockWrite = true;
5457
5458 /* Search for image with allocated block. Do not attempt to
5459 * read more than the previous reads marked as valid. Otherwise
5460 * this would return stale data when different block sizes are
5461 * used for the images. */
5462 for (PVDIMAGE pCurrImage = pImageFrom;
5463 pCurrImage != NULL && pCurrImage != pImageTo && rc == VERR_VD_BLOCK_FREE;
5464 pCurrImage = pCurrImage->pPrev)
5465 {
5466 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
5467 uOffset, pvBuf,
5468 cbThisRead, &cbThisRead);
5469 }
5470
5471 if (rc != VERR_VD_BLOCK_FREE)
5472 {
5473 if (RT_FAILURE(rc))
5474 break;
5475 rc = vdWriteHelper(pDisk, pImageTo, NULL, uOffset, pvBuf,
5476 cbThisRead, true /* fUpdateCache */);
5477 if (RT_FAILURE(rc))
5478 break;
5479 }
5480 else
5481 rc = VINF_SUCCESS;
5482
5483 rc2 = vdThreadFinishWrite(pDisk);
5484 AssertRC(rc2);
5485 fLockWrite = false;
5486
5487 uOffset += cbThisRead;
5488 cbRemaining -= cbThisRead;
5489
5490 if (pCbProgress && pCbProgress->pfnProgress)
5491 {
5492 /** @todo r=klaus: this can update the progress to the same
5493 * percentage over and over again if the image format makes
5494 * relatively small increments. */
5495 rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
5496 uOffset * 99 / cbSize);
5497 if (RT_FAILURE(rc))
5498 break;
5499 }
5500 } while (uOffset < cbSize);
5501
5502 /* In case we set up a "write proxy" image above we must clear
5503 * this again now to prevent stray writes. Failure or not. */
5504 if (!pImageFrom->pNext)
5505 {
5506 /* Take the write lock. */
5507 rc2 = vdThreadStartWrite(pDisk);
5508 AssertRC(rc2);
5509 fLockWrite = true;
5510
5511 pDisk->pImageRelay = NULL;
5512
5513 rc2 = vdThreadFinishWrite(pDisk);
5514 AssertRC(rc2);
5515 fLockWrite = false;
5516 }
5517 }
5518
5519 /*
5520 * Leave in case of an error to avoid corrupted data in the image chain
5521 * (includes cancelling the operation by the user).
5522 */
5523 if (RT_FAILURE(rc))
5524 break;
5525
5526 /* Need to hold the write lock while finishing the merge. */
5527 rc2 = vdThreadStartWrite(pDisk);
5528 AssertRC(rc2);
5529 fLockWrite = true;
5530
5531 /* Update parent UUID so that image chain is consistent. */
5532 RTUUID Uuid;
5533 PVDIMAGE pImageChild = NULL;
5534 if (nImageFrom < nImageTo)
5535 {
5536 if (pImageFrom->pPrev)
5537 {
5538 rc = pImageFrom->pPrev->Backend->pfnGetUuid(pImageFrom->pPrev->pBackendData,
5539 &Uuid);
5540 AssertRC(rc);
5541 }
5542 else
5543 RTUuidClear(&Uuid);
5544 rc = pImageTo->Backend->pfnSetParentUuid(pImageTo->pBackendData,
5545 &Uuid);
5546 AssertRC(rc);
5547 }
5548 else
5549 {
5550 /* Update the parent uuid of the child of the last merged image. */
5551 if (pImageFrom->pNext)
5552 {
5553 rc = pImageTo->Backend->pfnGetUuid(pImageTo->pBackendData,
5554 &Uuid);
5555 AssertRC(rc);
5556
5557 rc = pImageFrom->Backend->pfnSetParentUuid(pImageFrom->pNext->pBackendData,
5558 &Uuid);
5559 AssertRC(rc);
5560
5561 pImageChild = pImageFrom->pNext;
5562 }
5563 }
5564
5565 /* Delete the no longer needed images. */
5566 PVDIMAGE pImg = pImageFrom, pTmp;
5567 while (pImg != pImageTo)
5568 {
5569 if (nImageFrom < nImageTo)
5570 pTmp = pImg->pNext;
5571 else
5572 pTmp = pImg->pPrev;
5573 vdRemoveImageFromList(pDisk, pImg);
5574 pImg->Backend->pfnClose(pImg->pBackendData, true);
5575 RTMemFree(pImg->pszFilename);
5576 RTMemFree(pImg);
5577 pImg = pTmp;
5578 }
5579
5580 /* Make sure destination image is back to read only if necessary. */
5581 if (pImageTo != pDisk->pLast)
5582 {
5583 uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pBackendData);
5584 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
5585 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
5586 uOpenFlags);
5587 if (RT_FAILURE(rc))
5588 break;
5589 }
5590
5591 /*
5592 * Make sure the child is readonly
5593 * for the child -> parent merge direction
5594 * if necessary.
5595 */
5596 if ( nImageFrom > nImageTo
5597 && pImageChild
5598 && pImageChild != pDisk->pLast)
5599 {
5600 uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pBackendData);
5601 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
5602 rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pBackendData,
5603 uOpenFlags);
5604 if (RT_FAILURE(rc))
5605 break;
5606 }
5607 } while (0);
5608
5609 if (RT_UNLIKELY(fLockWrite))
5610 {
5611 rc2 = vdThreadFinishWrite(pDisk);
5612 AssertRC(rc2);
5613 }
5614 else if (RT_UNLIKELY(fLockRead))
5615 {
5616 rc2 = vdThreadFinishRead(pDisk);
5617 AssertRC(rc2);
5618 }
5619
5620 if (pvBuf)
5621 RTMemTmpFree(pvBuf);
5622
5623 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
5624 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
5625
5626 LogFlowFunc(("returns %Rrc\n", rc));
5627 return rc;
5628}
5629
5630/**
5631 * Copies an image from one HDD container to another.
5632 * The copy is opened in the target HDD container.
5633 * It is possible to convert between different image formats, because the
5634 * backend for the destination may be different from the source.
5635 * If both the source and destination reference the same HDD container,
5636 * then the image is moved (by copying/deleting or renaming) to the new location.
5637 * The source container is unchanged if the move operation fails, otherwise
5638 * the image at the new location is opened in the same way as the old one was.
5639 *
5640 * @returns VBox status code.
5641 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
5642 * @param pDiskFrom Pointer to source HDD container.
5643 * @param nImage Image number, counts from 0. 0 is always base image of container.
5644 * @param pDiskTo Pointer to destination HDD container.
5645 * @param pszBackend Name of the image file backend to use.
5646 * @param pszFilename New name of the image (may be NULL if pDiskFrom == pDiskTo).
5647 * @param fMoveByRename If true, attempt to perform a move by renaming (if successful the new size is ignored).
5648 * @param cbSize New image size (0 means leave unchanged).
5649 * @param uImageFlags Flags specifying special destination image features.
5650 * @param pDstUuid New UUID of the destination image. If NULL, a new UUID is created.
5651 * This parameter is used if and only if a true copy is created.
5652 * In all rename/move cases the UUIDs are copied over.
5653 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
5654 * Only used if the destination image is created.
5655 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
5656 * @param pDstVDIfsImage Pointer to the per-image VD interface list, for the
5657 * destination image.
5658 * @param pDstVDIfsOperation Pointer to the per-image VD interface list,
5659 * for the destination image.
5660 */
5661VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
5662 const char *pszBackend, const char *pszFilename,
5663 bool fMoveByRename, uint64_t cbSize,
5664 unsigned uImageFlags, PCRTUUID pDstUuid,
5665 unsigned uOpenFlags, PVDINTERFACE pVDIfsOperation,
5666 PVDINTERFACE pDstVDIfsImage,
5667 PVDINTERFACE pDstVDIfsOperation)
5668{
5669 int rc = VINF_SUCCESS;
5670 int rc2;
5671 bool fLockReadFrom = false, fLockWriteFrom = false, fLockWriteTo = false;
5672 void *pvBuf = NULL;
5673 PVDIMAGE pImageTo = NULL;
5674
5675 LogFlowFunc(("pDiskFrom=%#p nImage=%u pDiskTo=%#p pszBackend=\"%s\" pszFilename=\"%s\" fMoveByRename=%d cbSize=%llu uImageFlags=%#x pDstUuid=%#p uOpenFlags=%#x pVDIfsOperation=%#p pDstVDIfsImage=%#p pDstVDIfsOperation=%#p\n",
5676 pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename, cbSize, uImageFlags, pDstUuid, uOpenFlags, pVDIfsOperation, pDstVDIfsImage, pDstVDIfsOperation));
5677
5678 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
5679 VDINTERFACETYPE_PROGRESS);
5680 PVDINTERFACEPROGRESS pCbProgress = NULL;
5681 if (pIfProgress)
5682 pCbProgress = VDGetInterfaceProgress(pIfProgress);
5683
5684 PVDINTERFACE pDstIfProgress = VDInterfaceGet(pDstVDIfsOperation,
5685 VDINTERFACETYPE_PROGRESS);
5686 PVDINTERFACEPROGRESS pDstCbProgress = NULL;
5687 if (pDstIfProgress)
5688 pDstCbProgress = VDGetInterfaceProgress(pDstIfProgress);
5689
5690 do {
5691 /* Check arguments. */
5692 AssertMsgBreakStmt(VALID_PTR(pDiskFrom), ("pDiskFrom=%#p\n", pDiskFrom),
5693 rc = VERR_INVALID_PARAMETER);
5694 AssertMsg(pDiskFrom->u32Signature == VBOXHDDDISK_SIGNATURE,
5695 ("u32Signature=%08x\n", pDiskFrom->u32Signature));
5696
5697 rc2 = vdThreadStartRead(pDiskFrom);
5698 AssertRC(rc2);
5699 fLockReadFrom = true;
5700 PVDIMAGE pImageFrom = vdGetImageByNumber(pDiskFrom, nImage);
5701 AssertPtrBreakStmt(pImageFrom, rc = VERR_VD_IMAGE_NOT_FOUND);
5702 AssertMsgBreakStmt(VALID_PTR(pDiskTo), ("pDiskTo=%#p\n", pDiskTo),
5703 rc = VERR_INVALID_PARAMETER);
5704 AssertMsg(pDiskTo->u32Signature == VBOXHDDDISK_SIGNATURE,
5705 ("u32Signature=%08x\n", pDiskTo->u32Signature));
5706
5707 /* Move the image. */
5708 if (pDiskFrom == pDiskTo)
5709 {
5710 /* Rename only works when backends are the same, are file based
5711 * and the rename method is implemented. */
5712 if ( fMoveByRename
5713 && !RTStrICmp(pszBackend, pImageFrom->Backend->pszBackendName)
5714 && pImageFrom->Backend->uBackendCaps & VD_CAP_FILE
5715 && pImageFrom->Backend->pfnRename)
5716 {
5717 rc2 = vdThreadFinishRead(pDiskFrom);
5718 AssertRC(rc2);
5719 fLockReadFrom = false;
5720
5721 rc2 = vdThreadStartWrite(pDiskFrom);
5722 AssertRC(rc2);
5723 fLockWriteFrom = true;
5724 rc = pImageFrom->Backend->pfnRename(pImageFrom->pBackendData, pszFilename ? pszFilename : pImageFrom->pszFilename);
5725 break;
5726 }
5727
5728 /** @todo Moving (including shrinking/growing) of the image is
5729 * requested, but the rename attempt failed or it wasn't possible.
5730 * Must now copy image to temp location. */
5731 AssertReleaseMsgFailed(("VDCopy: moving by copy/delete not implemented\n"));
5732 }
5733
5734 /* pszFilename is allowed to be NULL, as this indicates copy to the existing image. */
5735 AssertMsgBreakStmt(pszFilename == NULL || (VALID_PTR(pszFilename) && *pszFilename),
5736 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
5737 rc = VERR_INVALID_PARAMETER);
5738
5739 uint64_t cbSizeFrom;
5740 cbSizeFrom = pImageFrom->Backend->pfnGetSize(pImageFrom->pBackendData);
5741 if (cbSizeFrom == 0)
5742 {
5743 rc = VERR_VD_VALUE_NOT_FOUND;
5744 break;
5745 }
5746
5747 VDGEOMETRY PCHSGeometryFrom = {0, 0, 0};
5748 VDGEOMETRY LCHSGeometryFrom = {0, 0, 0};
5749 pImageFrom->Backend->pfnGetPCHSGeometry(pImageFrom->pBackendData, &PCHSGeometryFrom);
5750 pImageFrom->Backend->pfnGetLCHSGeometry(pImageFrom->pBackendData, &LCHSGeometryFrom);
5751
5752 RTUUID ImageUuid, ImageModificationUuid;
5753 if (pDiskFrom != pDiskTo)
5754 {
5755 if (pDstUuid)
5756 ImageUuid = *pDstUuid;
5757 else
5758 RTUuidCreate(&ImageUuid);
5759 }
5760 else
5761 {
5762 rc = pImageFrom->Backend->pfnGetUuid(pImageFrom->pBackendData, &ImageUuid);
5763 if (RT_FAILURE(rc))
5764 RTUuidCreate(&ImageUuid);
5765 }
5766 rc = pImageFrom->Backend->pfnGetModificationUuid(pImageFrom->pBackendData, &ImageModificationUuid);
5767 if (RT_FAILURE(rc))
5768 RTUuidClear(&ImageModificationUuid);
5769
5770 char szComment[1024];
5771 rc = pImageFrom->Backend->pfnGetComment(pImageFrom->pBackendData, szComment, sizeof(szComment));
5772 if (RT_FAILURE(rc))
5773 szComment[0] = '\0';
5774 else
5775 szComment[sizeof(szComment) - 1] = '\0';
5776
5777 rc2 = vdThreadFinishRead(pDiskFrom);
5778 AssertRC(rc2);
5779 fLockReadFrom = false;
5780
5781 rc2 = vdThreadStartRead(pDiskTo);
5782 AssertRC(rc2);
5783 unsigned cImagesTo = pDiskTo->cImages;
5784 rc2 = vdThreadFinishRead(pDiskTo);
5785 AssertRC(rc2);
5786
5787 if (pszFilename)
5788 {
5789 if (cbSize == 0)
5790 cbSize = cbSizeFrom;
5791
5792 /* Create destination image with the properties of source image. */
5793 /** @todo replace the VDCreateDiff/VDCreateBase calls by direct
5794 * calls to the backend. Unifies the code and reduces the API
5795 * dependencies. Would also make the synchronization explicit. */
5796 if (cImagesTo > 0)
5797 {
5798 rc = VDCreateDiff(pDiskTo, pszBackend, pszFilename,
5799 uImageFlags, szComment, &ImageUuid,
5800 NULL /* pParentUuid */,
5801 uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
5802 pDstVDIfsImage, NULL);
5803
5804 rc2 = vdThreadStartWrite(pDiskTo);
5805 AssertRC(rc2);
5806 fLockWriteTo = true;
5807 } else {
5808 /** @todo hack to force creation of a fixed image for
5809 * the RAW backend, which can't handle anything else. */
5810 if (!RTStrICmp(pszBackend, "RAW"))
5811 uImageFlags |= VD_IMAGE_FLAGS_FIXED;
5812
5813 vdFixupPCHSGeometry(&PCHSGeometryFrom, cbSize);
5814 vdFixupLCHSGeometry(&LCHSGeometryFrom, cbSize);
5815
5816 rc = VDCreateBase(pDiskTo, pszBackend, pszFilename, cbSize,
5817 uImageFlags, szComment,
5818 &PCHSGeometryFrom, &LCHSGeometryFrom,
5819 NULL, uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
5820 pDstVDIfsImage, NULL);
5821
5822 rc2 = vdThreadStartWrite(pDiskTo);
5823 AssertRC(rc2);
5824 fLockWriteTo = true;
5825
5826 if (RT_SUCCESS(rc) && !RTUuidIsNull(&ImageUuid))
5827 pDiskTo->pLast->Backend->pfnSetUuid(pDiskTo->pLast->pBackendData, &ImageUuid);
5828 }
5829 if (RT_FAILURE(rc))
5830 break;
5831
5832 pImageTo = pDiskTo->pLast;
5833 AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
5834
5835 cbSize = RT_MIN(cbSize, cbSizeFrom);
5836 }
5837 else
5838 {
5839 pImageTo = pDiskTo->pLast;
5840 AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
5841
5842 uint64_t cbSizeTo;
5843 cbSizeTo = pImageTo->Backend->pfnGetSize(pImageTo->pBackendData);
5844 if (cbSizeTo == 0)
5845 {
5846 rc = VERR_VD_VALUE_NOT_FOUND;
5847 break;
5848 }
5849
5850 if (cbSize == 0)
5851 cbSize = RT_MIN(cbSizeFrom, cbSizeTo);
5852
5853 vdFixupPCHSGeometry(&PCHSGeometryFrom, cbSize);
5854 vdFixupLCHSGeometry(&LCHSGeometryFrom, cbSize);
5855
5856 /* Update the geometry in the destination image. */
5857 pImageTo->Backend->pfnSetPCHSGeometry(pImageTo->pBackendData, &PCHSGeometryFrom);
5858 pImageTo->Backend->pfnSetLCHSGeometry(pImageTo->pBackendData, &LCHSGeometryFrom);
5859 }
5860
5861 rc2 = vdThreadFinishWrite(pDiskTo);
5862 AssertRC(rc2);
5863 fLockWriteTo = false;
5864
5865 /* Allocate tmp buffer. */
5866 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
5867 if (!pvBuf)
5868 {
5869 rc = VERR_NO_MEMORY;
5870 break;
5871 }
5872
5873 /* Whether we can take the optimized copy path (false) or not.
5874 * Don't optimize if the image existed or if it is a child image. */
5875 bool fRegularRead = (pszFilename == NULL) || (cImagesTo > 0);
5876
5877 /* Copy the data. */
5878 uint64_t uOffset = 0;
5879 uint64_t cbRemaining = cbSize;
5880
5881 do
5882 {
5883 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
5884
5885 /* Note that we don't attempt to synchronize cross-disk accesses.
5886 * It wouldn't be very difficult to do, just the lock order would
5887 * need to be defined somehow to prevent deadlocks. Postpone such
5888 * magic as there is no use case for this. */
5889
5890 rc2 = vdThreadStartRead(pDiskFrom);
5891 AssertRC(rc2);
5892 fLockReadFrom = true;
5893
5894 /*
5895 * Updating the cache doesn't make any sense
5896 * as we are looping once through the image.
5897 */
5898 rc = vdReadHelper(pDiskFrom, pImageFrom, NULL, uOffset, pvBuf,
5899 cbThisRead, fRegularRead,
5900 false /* fUpdateCache */);
5901 if (RT_FAILURE(rc) && rc != VERR_VD_BLOCK_FREE)
5902 break;
5903
5904 rc2 = vdThreadFinishRead(pDiskFrom);
5905 AssertRC(rc2);
5906 fLockReadFrom = false;
5907
5908 if (rc != VERR_VD_BLOCK_FREE)
5909 {
5910 rc2 = vdThreadStartWrite(pDiskTo);
5911 AssertRC(rc2);
5912 fLockWriteTo = true;
5913
5914 rc = vdWriteHelper(pDiskTo, pImageTo, NULL, uOffset, pvBuf,
5915 cbThisRead, false /* fUpdateCache */);
5916 if (RT_FAILURE(rc))
5917 break;
5918
5919 rc2 = vdThreadFinishWrite(pDiskTo);
5920 AssertRC(rc2);
5921 fLockWriteTo = false;
5922 }
5923 else /* Don't propagate the error to the outside */
5924 rc = VINF_SUCCESS;
5925
5926 uOffset += cbThisRead;
5927 cbRemaining -= cbThisRead;
5928
5929 if (pCbProgress && pCbProgress->pfnProgress)
5930 {
5931 /** @todo r=klaus: this can update the progress to the same
5932 * percentage over and over again if the image format makes
5933 * relatively small increments. */
5934 rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
5935 uOffset * 99 / cbSize);
5936 if (RT_FAILURE(rc))
5937 break;
5938 }
5939 if (pDstCbProgress && pDstCbProgress->pfnProgress)
5940 {
5941 /** @todo r=klaus: this can update the progress to the same
5942 * percentage over and over again if the image format makes
5943 * relatively small increments. */
5944 rc = pDstCbProgress->pfnProgress(pDstIfProgress->pvUser,
5945 uOffset * 99 / cbSize);
5946 if (RT_FAILURE(rc))
5947 break;
5948 }
5949 } while (uOffset < cbSize);
5950
5951 if (RT_SUCCESS(rc))
5952 {
5953 rc2 = vdThreadStartWrite(pDiskTo);
5954 AssertRC(rc2);
5955 fLockWriteTo = true;
5956
5957 /* Only set modification UUID if it is non-null, since the source
5958 * backend might not provide a valid modification UUID. */
5959 if (!RTUuidIsNull(&ImageModificationUuid))
5960 pImageTo->Backend->pfnSetModificationUuid(pImageTo->pBackendData, &ImageModificationUuid);
5961
5962 /* Set the requested open flags if they differ from the value
5963 * required for creating the image and copying the contents. */
5964 if ( pImageTo && pszFilename
5965 && uOpenFlags != (uOpenFlags & ~VD_OPEN_FLAGS_READONLY))
5966 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
5967 uOpenFlags);
5968 }
5969 } while (0);
5970
5971 if (RT_FAILURE(rc) && pImageTo && pszFilename)
5972 {
5973 /* Take the write lock only if it is not taken. Not worth making the
5974 * above code even more complicated. */
5975 if (RT_UNLIKELY(!fLockWriteTo))
5976 {
5977 rc2 = vdThreadStartWrite(pDiskTo);
5978 AssertRC(rc2);
5979 fLockWriteTo = true;
5980 }
5981 /* Error detected, but new image created. Remove image from list. */
5982 vdRemoveImageFromList(pDiskTo, pImageTo);
5983
5984 /* Close and delete image. */
5985 rc2 = pImageTo->Backend->pfnClose(pImageTo->pBackendData, true);
5986 AssertRC(rc2);
5987 pImageTo->pBackendData = NULL;
5988
5989 /* Free remaining resources. */
5990 if (pImageTo->pszFilename)
5991 RTStrFree(pImageTo->pszFilename);
5992
5993 RTMemFree(pImageTo);
5994 }
5995
5996 if (RT_UNLIKELY(fLockWriteTo))
5997 {
5998 rc2 = vdThreadFinishWrite(pDiskTo);
5999 AssertRC(rc2);
6000 }
6001 if (RT_UNLIKELY(fLockWriteFrom))
6002 {
6003 rc2 = vdThreadFinishWrite(pDiskFrom);
6004 AssertRC(rc2);
6005 }
6006 else if (RT_UNLIKELY(fLockReadFrom))
6007 {
6008 rc2 = vdThreadFinishRead(pDiskFrom);
6009 AssertRC(rc2);
6010 }
6011
6012 if (pvBuf)
6013 RTMemTmpFree(pvBuf);
6014
6015 if (RT_SUCCESS(rc))
6016 {
6017 if (pCbProgress && pCbProgress->pfnProgress)
6018 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
6019 if (pDstCbProgress && pDstCbProgress->pfnProgress)
6020 pDstCbProgress->pfnProgress(pDstIfProgress->pvUser, 100);
6021 }
6022
6023 LogFlowFunc(("returns %Rrc\n", rc));
6024 return rc;
6025}
6026
6027/**
6028 * Optimizes the storage consumption of an image. Typically the unused blocks
6029 * have to be wiped with zeroes to achieve a substantial reduced storage use.
6030 * Another optimization done is reordering the image blocks, which can provide
6031 * a significant performance boost, as reads and writes tend to use less random
6032 * file offsets.
6033 *
6034 * @return VBox status code.
6035 * @return VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
6036 * @return VERR_VD_IMAGE_READ_ONLY if image is not writable.
6037 * @return VERR_NOT_SUPPORTED if this kind of image can be compacted, but
6038 * the code for this isn't implemented yet.
6039 * @param pDisk Pointer to HDD container.
6040 * @param nImage Image number, counts from 0. 0 is always base image of container.
6041 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
6042 */
6043VBOXDDU_DECL(int) VDCompact(PVBOXHDD pDisk, unsigned nImage,
6044 PVDINTERFACE pVDIfsOperation)
6045{
6046 int rc = VINF_SUCCESS;
6047 int rc2;
6048 bool fLockRead = false, fLockWrite = false;
6049 void *pvBuf = NULL;
6050 void *pvTmp = NULL;
6051
6052 LogFlowFunc(("pDisk=%#p nImage=%u pVDIfsOperation=%#p\n",
6053 pDisk, nImage, pVDIfsOperation));
6054
6055 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
6056 VDINTERFACETYPE_PROGRESS);
6057 PVDINTERFACEPROGRESS pCbProgress = NULL;
6058 if (pIfProgress)
6059 pCbProgress = VDGetInterfaceProgress(pIfProgress);
6060
6061 do {
6062 /* Check arguments. */
6063 AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
6064 rc = VERR_INVALID_PARAMETER);
6065 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE,
6066 ("u32Signature=%08x\n", pDisk->u32Signature));
6067
6068 rc2 = vdThreadStartRead(pDisk);
6069 AssertRC(rc2);
6070 fLockRead = true;
6071
6072 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6073 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
6074
6075 /* If there is no compact callback for not file based backends then
6076 * the backend doesn't need compaction. No need to make much fuss about
6077 * this. For file based ones signal this as not yet supported. */
6078 if (!pImage->Backend->pfnCompact)
6079 {
6080 if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
6081 rc = VERR_NOT_SUPPORTED;
6082 else
6083 rc = VINF_SUCCESS;
6084 break;
6085 }
6086
6087 /* Insert interface for reading parent state into per-operation list,
6088 * if there is a parent image. */
6089 VDINTERFACE IfOpParent;
6090 VDINTERFACEPARENTSTATE ParentCb;
6091 VDPARENTSTATEDESC ParentUser;
6092 if (pImage->pPrev)
6093 {
6094 ParentCb.cbSize = sizeof(ParentCb);
6095 ParentCb.enmInterface = VDINTERFACETYPE_PARENTSTATE;
6096 ParentCb.pfnParentRead = vdParentRead;
6097 ParentUser.pDisk = pDisk;
6098 ParentUser.pImage = pImage->pPrev;
6099 rc = VDInterfaceAdd(&IfOpParent, "VDCompact_ParentState", VDINTERFACETYPE_PARENTSTATE,
6100 &ParentCb, &ParentUser, &pVDIfsOperation);
6101 AssertRC(rc);
6102 }
6103
6104 rc2 = vdThreadFinishRead(pDisk);
6105 AssertRC(rc2);
6106 fLockRead = false;
6107
6108 rc2 = vdThreadStartWrite(pDisk);
6109 AssertRC(rc2);
6110 fLockWrite = true;
6111
6112 rc = pImage->Backend->pfnCompact(pImage->pBackendData,
6113 0, 99,
6114 pDisk->pVDIfsDisk,
6115 pImage->pVDIfsImage,
6116 pVDIfsOperation);
6117 } while (0);
6118
6119 if (RT_UNLIKELY(fLockWrite))
6120 {
6121 rc2 = vdThreadFinishWrite(pDisk);
6122 AssertRC(rc2);
6123 }
6124 else if (RT_UNLIKELY(fLockRead))
6125 {
6126 rc2 = vdThreadFinishRead(pDisk);
6127 AssertRC(rc2);
6128 }
6129
6130 if (pvBuf)
6131 RTMemTmpFree(pvBuf);
6132 if (pvTmp)
6133 RTMemTmpFree(pvTmp);
6134
6135 if (RT_SUCCESS(rc))
6136 {
6137 if (pCbProgress && pCbProgress->pfnProgress)
6138 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
6139 }
6140
6141 LogFlowFunc(("returns %Rrc\n", rc));
6142 return rc;
6143}
6144
6145/**
6146 * Resizes the the given disk image to the given size.
6147 *
6148 * @return VBox status
6149 * @return VERR_VD_IMAGE_READ_ONLY if image is not writable.
6150 * @return VERR_NOT_SUPPORTED if this kind of image can be compacted, but
6151 *
6152 * @param pDisk Pointer to the HDD container.
6153 * @param cbSize New size of the image.
6154 * @param pPCHSGeometry Pointer to the new physical disk geometry <= (16383,16,63). Not NULL.
6155 * @param pLCHSGeometry Pointer to the new logical disk geometry <= (x,255,63). Not NULL.
6156 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
6157 */
6158VBOXDDU_DECL(int) VDResize(PVBOXHDD pDisk, uint64_t cbSize,
6159 PCVDGEOMETRY pPCHSGeometry,
6160 PCVDGEOMETRY pLCHSGeometry,
6161 PVDINTERFACE pVDIfsOperation)
6162{
6163 /** @todo r=klaus resizing was designed to be part of VDCopy, so having a separate function is not desirable. */
6164 int rc = VINF_SUCCESS;
6165 int rc2;
6166 bool fLockRead = false, fLockWrite = false;
6167
6168 LogFlowFunc(("pDisk=%#p cbSize=%llu pVDIfsOperation=%#p\n",
6169 pDisk, cbSize, pVDIfsOperation));
6170
6171 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
6172 VDINTERFACETYPE_PROGRESS);
6173 PVDINTERFACEPROGRESS pCbProgress = NULL;
6174 if (pIfProgress)
6175 pCbProgress = VDGetInterfaceProgress(pIfProgress);
6176
6177 do {
6178 /* Check arguments. */
6179 AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
6180 rc = VERR_INVALID_PARAMETER);
6181 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE,
6182 ("u32Signature=%08x\n", pDisk->u32Signature));
6183
6184 rc2 = vdThreadStartRead(pDisk);
6185 AssertRC(rc2);
6186 fLockRead = true;
6187
6188 /* Not supported if the disk has child images attached. */
6189 AssertMsgBreakStmt(pDisk->cImages == 1, ("cImages=%u\n", pDisk->cImages),
6190 rc = VERR_NOT_SUPPORTED);
6191
6192 PVDIMAGE pImage = pDisk->pBase;
6193
6194 /* If there is no compact callback for not file based backends then
6195 * the backend doesn't need compaction. No need to make much fuss about
6196 * this. For file based ones signal this as not yet supported. */
6197 if (!pImage->Backend->pfnResize)
6198 {
6199 if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
6200 rc = VERR_NOT_SUPPORTED;
6201 else
6202 rc = VINF_SUCCESS;
6203 break;
6204 }
6205
6206 rc2 = vdThreadFinishRead(pDisk);
6207 AssertRC(rc2);
6208 fLockRead = false;
6209
6210 rc2 = vdThreadStartWrite(pDisk);
6211 AssertRC(rc2);
6212 fLockWrite = true;
6213
6214 VDGEOMETRY PCHSGeometryOld;
6215 VDGEOMETRY LCHSGeometryOld;
6216 PCVDGEOMETRY pPCHSGeometryNew;
6217 PCVDGEOMETRY pLCHSGeometryNew;
6218
6219 if (pPCHSGeometry->cCylinders == 0)
6220 {
6221 /* Auto-detect marker, calculate new value ourself. */
6222 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData, &PCHSGeometryOld);
6223 if (RT_SUCCESS(rc) && (PCHSGeometryOld.cCylinders != 0))
6224 PCHSGeometryOld.cCylinders = RT_MIN(cbSize / 512 / PCHSGeometryOld.cHeads / PCHSGeometryOld.cSectors, 16383);
6225 else if (rc == VERR_VD_GEOMETRY_NOT_SET)
6226 rc = VINF_SUCCESS;
6227
6228 pPCHSGeometryNew = &PCHSGeometryOld;
6229 }
6230 else
6231 pPCHSGeometryNew = pPCHSGeometry;
6232
6233 if (pLCHSGeometry->cCylinders == 0)
6234 {
6235 /* Auto-detect marker, calculate new value ourself. */
6236 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData, &LCHSGeometryOld);
6237 if (RT_SUCCESS(rc) && (LCHSGeometryOld.cCylinders != 0))
6238 LCHSGeometryOld.cCylinders = cbSize / 512 / LCHSGeometryOld.cHeads / LCHSGeometryOld.cSectors;
6239 else if (rc == VERR_VD_GEOMETRY_NOT_SET)
6240 rc = VINF_SUCCESS;
6241
6242 pLCHSGeometryNew = &LCHSGeometryOld;
6243 }
6244 else
6245 pLCHSGeometryNew = pLCHSGeometry;
6246
6247 if (RT_SUCCESS(rc))
6248 rc = pImage->Backend->pfnResize(pImage->pBackendData,
6249 cbSize,
6250 pPCHSGeometryNew,
6251 pLCHSGeometryNew,
6252 0, 99,
6253 pDisk->pVDIfsDisk,
6254 pImage->pVDIfsImage,
6255 pVDIfsOperation);
6256 } while (0);
6257
6258 if (RT_UNLIKELY(fLockWrite))
6259 {
6260 rc2 = vdThreadFinishWrite(pDisk);
6261 AssertRC(rc2);
6262 }
6263 else if (RT_UNLIKELY(fLockRead))
6264 {
6265 rc2 = vdThreadFinishRead(pDisk);
6266 AssertRC(rc2);
6267 }
6268
6269 if (RT_SUCCESS(rc))
6270 {
6271 if (pCbProgress && pCbProgress->pfnProgress)
6272 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
6273 }
6274
6275 LogFlowFunc(("returns %Rrc\n", rc));
6276 return rc;
6277}
6278
6279/**
6280 * Closes the last opened image file in HDD container.
6281 * If previous image file was opened in read-only mode (the normal case) and
6282 * the last opened image is in read-write mode then the previous image will be
6283 * reopened in read/write mode.
6284 *
6285 * @returns VBox status code.
6286 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
6287 * @param pDisk Pointer to HDD container.
6288 * @param fDelete If true, delete the image from the host disk.
6289 */
6290VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete)
6291{
6292 int rc = VINF_SUCCESS;
6293 int rc2;
6294 bool fLockWrite = false;
6295
6296 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
6297 do
6298 {
6299 /* sanity check */
6300 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6301 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6302
6303 /* Not worth splitting this up into a read lock phase and write
6304 * lock phase, as closing an image is a relatively fast operation
6305 * dominated by the part which needs the write lock. */
6306 rc2 = vdThreadStartWrite(pDisk);
6307 AssertRC(rc2);
6308 fLockWrite = true;
6309
6310 PVDIMAGE pImage = pDisk->pLast;
6311 if (!pImage)
6312 {
6313 rc = VERR_VD_NOT_OPENED;
6314 break;
6315 }
6316 unsigned uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
6317 /* Remove image from list of opened images. */
6318 vdRemoveImageFromList(pDisk, pImage);
6319 /* Close (and optionally delete) image. */
6320 rc = pImage->Backend->pfnClose(pImage->pBackendData, fDelete);
6321 /* Free remaining resources related to the image. */
6322 RTStrFree(pImage->pszFilename);
6323 RTMemFree(pImage);
6324
6325 pImage = pDisk->pLast;
6326 if (!pImage)
6327 break;
6328
6329 /* If disk was previously in read/write mode, make sure it will stay
6330 * like this (if possible) after closing this image. Set the open flags
6331 * accordingly. */
6332 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
6333 {
6334 uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
6335 uOpenFlags &= ~ VD_OPEN_FLAGS_READONLY;
6336 rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData, uOpenFlags);
6337 }
6338
6339 /* Cache disk information. */
6340 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
6341
6342 /* Cache PCHS geometry. */
6343 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
6344 &pDisk->PCHSGeometry);
6345 if (RT_FAILURE(rc2))
6346 {
6347 pDisk->PCHSGeometry.cCylinders = 0;
6348 pDisk->PCHSGeometry.cHeads = 0;
6349 pDisk->PCHSGeometry.cSectors = 0;
6350 }
6351 else
6352 {
6353 /* Make sure the PCHS geometry is properly clipped. */
6354 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
6355 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
6356 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
6357 }
6358
6359 /* Cache LCHS geometry. */
6360 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
6361 &pDisk->LCHSGeometry);
6362 if (RT_FAILURE(rc2))
6363 {
6364 pDisk->LCHSGeometry.cCylinders = 0;
6365 pDisk->LCHSGeometry.cHeads = 0;
6366 pDisk->LCHSGeometry.cSectors = 0;
6367 }
6368 else
6369 {
6370 /* Make sure the LCHS geometry is properly clipped. */
6371 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
6372 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
6373 }
6374 } while (0);
6375
6376 if (RT_UNLIKELY(fLockWrite))
6377 {
6378 rc2 = vdThreadFinishWrite(pDisk);
6379 AssertRC(rc2);
6380 }
6381
6382 LogFlowFunc(("returns %Rrc\n", rc));
6383 return rc;
6384}
6385
6386/**
6387 * Closes the currently opened cache image file in HDD container.
6388 *
6389 * @return VBox status code.
6390 * @return VERR_VD_NOT_OPENED if no cache is opened in HDD container.
6391 * @param pDisk Pointer to HDD container.
6392 * @param fDelete If true, delete the image from the host disk.
6393 */
6394VBOXDDU_DECL(int) VDCacheClose(PVBOXHDD pDisk, bool fDelete)
6395{
6396 int rc = VINF_SUCCESS;
6397 int rc2;
6398 bool fLockWrite = false;
6399 PVDCACHE pCache = NULL;
6400
6401 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
6402
6403 do
6404 {
6405 /* sanity check */
6406 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6407 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6408
6409 rc2 = vdThreadStartWrite(pDisk);
6410 AssertRC(rc2);
6411 fLockWrite = true;
6412
6413 AssertPtrBreakStmt(pDisk->pCache, rc = VERR_VD_CACHE_NOT_FOUND);
6414
6415 pCache = pDisk->pCache;
6416 pDisk->pCache = NULL;
6417
6418 pCache->Backend->pfnClose(pCache->pBackendData, fDelete);
6419 if (pCache->pszFilename)
6420 RTStrFree(pCache->pszFilename);
6421 RTMemFree(pCache);
6422 } while (0);
6423
6424 if (RT_LIKELY(fLockWrite))
6425 {
6426 rc2 = vdThreadFinishWrite(pDisk);
6427 AssertRC(rc2);
6428 }
6429
6430 LogFlowFunc(("returns %Rrc\n", rc));
6431 return rc;
6432}
6433
6434/**
6435 * Closes all opened image files in HDD container.
6436 *
6437 * @returns VBox status code.
6438 * @param pDisk Pointer to HDD container.
6439 */
6440VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk)
6441{
6442 int rc = VINF_SUCCESS;
6443 int rc2;
6444 bool fLockWrite = false;
6445
6446 LogFlowFunc(("pDisk=%#p\n", pDisk));
6447 do
6448 {
6449 /* sanity check */
6450 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6451 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6452
6453 /* Lock the entire operation. */
6454 rc2 = vdThreadStartWrite(pDisk);
6455 AssertRC(rc2);
6456 fLockWrite = true;
6457
6458 PVDCACHE pCache = pDisk->pCache;
6459 if (pCache)
6460 {
6461 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, false);
6462 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
6463 rc = rc2;
6464
6465 if (pCache->pszFilename)
6466 RTStrFree(pCache->pszFilename);
6467 RTMemFree(pCache);
6468 }
6469
6470 PVDIMAGE pImage = pDisk->pLast;
6471 while (VALID_PTR(pImage))
6472 {
6473 PVDIMAGE pPrev = pImage->pPrev;
6474 /* Remove image from list of opened images. */
6475 vdRemoveImageFromList(pDisk, pImage);
6476 /* Close image. */
6477 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, false);
6478 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
6479 rc = rc2;
6480 /* Free remaining resources related to the image. */
6481 RTStrFree(pImage->pszFilename);
6482 RTMemFree(pImage);
6483 pImage = pPrev;
6484 }
6485 Assert(!VALID_PTR(pDisk->pLast));
6486 } while (0);
6487
6488 if (RT_UNLIKELY(fLockWrite))
6489 {
6490 rc2 = vdThreadFinishWrite(pDisk);
6491 AssertRC(rc2);
6492 }
6493
6494 LogFlowFunc(("returns %Rrc\n", rc));
6495 return rc;
6496}
6497
6498/**
6499 * Read data from virtual HDD.
6500 *
6501 * @returns VBox status code.
6502 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
6503 * @param pDisk Pointer to HDD container.
6504 * @param uOffset Offset of first reading byte from start of disk.
6505 * @param pvBuf Pointer to buffer for reading data.
6506 * @param cbRead Number of bytes to read.
6507 */
6508VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuf,
6509 size_t cbRead)
6510{
6511 int rc = VINF_SUCCESS;
6512 int rc2;
6513 bool fLockRead = false;
6514
6515 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbRead=%zu\n",
6516 pDisk, uOffset, pvBuf, cbRead));
6517 do
6518 {
6519 /* sanity check */
6520 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6521 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6522
6523 /* Check arguments. */
6524 AssertMsgBreakStmt(VALID_PTR(pvBuf),
6525 ("pvBuf=%#p\n", pvBuf),
6526 rc = VERR_INVALID_PARAMETER);
6527 AssertMsgBreakStmt(cbRead,
6528 ("cbRead=%zu\n", cbRead),
6529 rc = VERR_INVALID_PARAMETER);
6530
6531 rc2 = vdThreadStartRead(pDisk);
6532 AssertRC(rc2);
6533 fLockRead = true;
6534
6535 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
6536 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
6537 uOffset, cbRead, pDisk->cbSize),
6538 rc = VERR_INVALID_PARAMETER);
6539
6540 PVDIMAGE pImage = pDisk->pLast;
6541 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
6542
6543 rc = vdReadHelper(pDisk, pImage, NULL, uOffset, pvBuf, cbRead,
6544 true /* fZeroFreeBlocks */,
6545 true /* fUpdateCache */);
6546 } while (0);
6547
6548 if (RT_UNLIKELY(fLockRead))
6549 {
6550 rc2 = vdThreadFinishRead(pDisk);
6551 AssertRC(rc2);
6552 }
6553
6554 LogFlowFunc(("returns %Rrc\n", rc));
6555 return rc;
6556}
6557
6558/**
6559 * Write data to virtual HDD.
6560 *
6561 * @returns VBox status code.
6562 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
6563 * @param pDisk Pointer to HDD container.
6564 * @param uOffset Offset of the first byte being
6565 * written from start of disk.
6566 * @param pvBuf Pointer to buffer for writing data.
6567 * @param cbWrite Number of bytes to write.
6568 */
6569VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf,
6570 size_t cbWrite)
6571{
6572 int rc = VINF_SUCCESS;
6573 int rc2;
6574 bool fLockWrite = false;
6575
6576 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbWrite=%zu\n",
6577 pDisk, uOffset, pvBuf, cbWrite));
6578 do
6579 {
6580 /* sanity check */
6581 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6582 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6583
6584 /* Check arguments. */
6585 AssertMsgBreakStmt(VALID_PTR(pvBuf),
6586 ("pvBuf=%#p\n", pvBuf),
6587 rc = VERR_INVALID_PARAMETER);
6588 AssertMsgBreakStmt(cbWrite,
6589 ("cbWrite=%zu\n", cbWrite),
6590 rc = VERR_INVALID_PARAMETER);
6591
6592 rc2 = vdThreadStartWrite(pDisk);
6593 AssertRC(rc2);
6594 fLockWrite = true;
6595
6596 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
6597 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
6598 uOffset, cbWrite, pDisk->cbSize),
6599 rc = VERR_INVALID_PARAMETER);
6600
6601 PVDIMAGE pImage = pDisk->pLast;
6602 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
6603
6604 vdSetModifiedFlag(pDisk);
6605 rc = vdWriteHelper(pDisk, pImage, NULL, uOffset, pvBuf, cbWrite,
6606 true /* fUpdateCache */);
6607 if (RT_FAILURE(rc))
6608 break;
6609
6610 /* If there is a merge (in the direction towards a parent) running
6611 * concurrently then we have to also "relay" the write to this parent,
6612 * as the merge position might be already past the position where
6613 * this write is going. The "context" of the write can come from the
6614 * natural chain, since merging either already did or will take care
6615 * of the "other" content which is might be needed to fill the block
6616 * to a full allocation size. The cache doesn't need to be touched
6617 * as this write is covered by the previous one. */
6618 if (RT_UNLIKELY(pDisk->pImageRelay))
6619 rc = vdWriteHelper(pDisk, pDisk->pImageRelay, NULL, uOffset,
6620 pvBuf, cbWrite, false /* fUpdateCache */);
6621 } while (0);
6622
6623 if (RT_UNLIKELY(fLockWrite))
6624 {
6625 rc2 = vdThreadFinishWrite(pDisk);
6626 AssertRC(rc2);
6627 }
6628
6629 LogFlowFunc(("returns %Rrc\n", rc));
6630 return rc;
6631}
6632
6633/**
6634 * Make sure the on disk representation of a virtual HDD is up to date.
6635 *
6636 * @returns VBox status code.
6637 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
6638 * @param pDisk Pointer to HDD container.
6639 */
6640VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
6641{
6642 int rc = VINF_SUCCESS;
6643 int rc2;
6644 bool fLockWrite = false;
6645
6646 LogFlowFunc(("pDisk=%#p\n", pDisk));
6647 do
6648 {
6649 /* sanity check */
6650 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6651 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6652
6653 rc2 = vdThreadStartWrite(pDisk);
6654 AssertRC(rc2);
6655 fLockWrite = true;
6656
6657 PVDIMAGE pImage = pDisk->pLast;
6658 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
6659
6660 vdResetModifiedFlag(pDisk);
6661 rc = pImage->Backend->pfnFlush(pImage->pBackendData);
6662
6663 if ( RT_SUCCESS(rc)
6664 && pDisk->pCache)
6665 rc = pDisk->pCache->Backend->pfnFlush(pDisk->pCache->pBackendData);
6666 } while (0);
6667
6668 if (RT_UNLIKELY(fLockWrite))
6669 {
6670 rc2 = vdThreadFinishWrite(pDisk);
6671 AssertRC(rc2);
6672 }
6673
6674 LogFlowFunc(("returns %Rrc\n", rc));
6675 return rc;
6676}
6677
6678/**
6679 * Get number of opened images in HDD container.
6680 *
6681 * @returns Number of opened images for HDD container. 0 if no images have been opened.
6682 * @param pDisk Pointer to HDD container.
6683 */
6684VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk)
6685{
6686 unsigned cImages;
6687 int rc2;
6688 bool fLockRead = false;
6689
6690 LogFlowFunc(("pDisk=%#p\n", pDisk));
6691 do
6692 {
6693 /* sanity check */
6694 AssertPtrBreakStmt(pDisk, cImages = 0);
6695 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6696
6697 rc2 = vdThreadStartRead(pDisk);
6698 AssertRC(rc2);
6699 fLockRead = true;
6700
6701 cImages = pDisk->cImages;
6702 } while (0);
6703
6704 if (RT_UNLIKELY(fLockRead))
6705 {
6706 rc2 = vdThreadFinishRead(pDisk);
6707 AssertRC(rc2);
6708 }
6709
6710 LogFlowFunc(("returns %u\n", cImages));
6711 return cImages;
6712}
6713
6714/**
6715 * Get read/write mode of HDD container.
6716 *
6717 * @returns Virtual disk ReadOnly status.
6718 * @returns true if no image is opened in HDD container.
6719 * @param pDisk Pointer to HDD container.
6720 */
6721VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
6722{
6723 bool fReadOnly;
6724 int rc2;
6725 bool fLockRead = false;
6726
6727 LogFlowFunc(("pDisk=%#p\n", pDisk));
6728 do
6729 {
6730 /* sanity check */
6731 AssertPtrBreakStmt(pDisk, fReadOnly = false);
6732 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6733
6734 rc2 = vdThreadStartRead(pDisk);
6735 AssertRC(rc2);
6736 fLockRead = true;
6737
6738 PVDIMAGE pImage = pDisk->pLast;
6739 AssertPtrBreakStmt(pImage, fReadOnly = true);
6740
6741 unsigned uOpenFlags;
6742 uOpenFlags = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
6743 fReadOnly = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
6744 } while (0);
6745
6746 if (RT_UNLIKELY(fLockRead))
6747 {
6748 rc2 = vdThreadFinishRead(pDisk);
6749 AssertRC(rc2);
6750 }
6751
6752 LogFlowFunc(("returns %d\n", fReadOnly));
6753 return fReadOnly;
6754}
6755
6756/**
6757 * Get total capacity of an image in HDD container.
6758 *
6759 * @returns Virtual disk size in bytes.
6760 * @returns 0 if no image with specified number was not opened.
6761 * @param pDisk Pointer to HDD container.
6762 * @param nImage Image number, counts from 0. 0 is always base image of container.
6763 */
6764VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk, unsigned nImage)
6765{
6766 uint64_t cbSize;
6767 int rc2;
6768 bool fLockRead = false;
6769
6770 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
6771 do
6772 {
6773 /* sanity check */
6774 AssertPtrBreakStmt(pDisk, cbSize = 0);
6775 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6776
6777 rc2 = vdThreadStartRead(pDisk);
6778 AssertRC(rc2);
6779 fLockRead = true;
6780
6781 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6782 AssertPtrBreakStmt(pImage, cbSize = 0);
6783 cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
6784 } while (0);
6785
6786 if (RT_UNLIKELY(fLockRead))
6787 {
6788 rc2 = vdThreadFinishRead(pDisk);
6789 AssertRC(rc2);
6790 }
6791
6792 LogFlowFunc(("returns %llu\n", cbSize));
6793 return cbSize;
6794}
6795
6796/**
6797 * Get total file size of an image in HDD container.
6798 *
6799 * @returns Virtual disk size in bytes.
6800 * @returns 0 if no image is opened in HDD container.
6801 * @param pDisk Pointer to HDD container.
6802 * @param nImage Image number, counts from 0. 0 is always base image of container.
6803 */
6804VBOXDDU_DECL(uint64_t) VDGetFileSize(PVBOXHDD pDisk, unsigned nImage)
6805{
6806 uint64_t cbSize;
6807 int rc2;
6808 bool fLockRead = false;
6809
6810 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
6811 do
6812 {
6813 /* sanity check */
6814 AssertPtrBreakStmt(pDisk, cbSize = 0);
6815 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6816
6817 rc2 = vdThreadStartRead(pDisk);
6818 AssertRC(rc2);
6819 fLockRead = true;
6820
6821 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6822 AssertPtrBreakStmt(pImage, cbSize = 0);
6823 cbSize = pImage->Backend->pfnGetFileSize(pImage->pBackendData);
6824 } while (0);
6825
6826 if (RT_UNLIKELY(fLockRead))
6827 {
6828 rc2 = vdThreadFinishRead(pDisk);
6829 AssertRC(rc2);
6830 }
6831
6832 LogFlowFunc(("returns %llu\n", cbSize));
6833 return cbSize;
6834}
6835
6836/**
6837 * Get virtual disk PCHS geometry stored in HDD container.
6838 *
6839 * @returns VBox status code.
6840 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
6841 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
6842 * @param pDisk Pointer to HDD container.
6843 * @param nImage Image number, counts from 0. 0 is always base image of container.
6844 * @param pPCHSGeometry Where to store PCHS geometry. Not NULL.
6845 */
6846VBOXDDU_DECL(int) VDGetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
6847 PVDGEOMETRY pPCHSGeometry)
6848{
6849 int rc = VINF_SUCCESS;
6850 int rc2;
6851 bool fLockRead = false;
6852
6853 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p\n",
6854 pDisk, nImage, pPCHSGeometry));
6855 do
6856 {
6857 /* sanity check */
6858 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6859 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6860
6861 /* Check arguments. */
6862 AssertMsgBreakStmt(VALID_PTR(pPCHSGeometry),
6863 ("pPCHSGeometry=%#p\n", pPCHSGeometry),
6864 rc = VERR_INVALID_PARAMETER);
6865
6866 rc2 = vdThreadStartRead(pDisk);
6867 AssertRC(rc2);
6868 fLockRead = true;
6869
6870 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6871 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
6872
6873 if (pImage == pDisk->pLast)
6874 {
6875 /* Use cached information if possible. */
6876 if (pDisk->PCHSGeometry.cCylinders != 0)
6877 *pPCHSGeometry = pDisk->PCHSGeometry;
6878 else
6879 rc = VERR_VD_GEOMETRY_NOT_SET;
6880 }
6881 else
6882 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
6883 pPCHSGeometry);
6884 } while (0);
6885
6886 if (RT_UNLIKELY(fLockRead))
6887 {
6888 rc2 = vdThreadFinishRead(pDisk);
6889 AssertRC(rc2);
6890 }
6891
6892 LogFlowFunc(("%Rrc (PCHS=%u/%u/%u)\n", rc,
6893 pDisk->PCHSGeometry.cCylinders, pDisk->PCHSGeometry.cHeads,
6894 pDisk->PCHSGeometry.cSectors));
6895 return rc;
6896}
6897
6898/**
6899 * Store virtual disk PCHS geometry in HDD container.
6900 *
6901 * Note that in case of unrecoverable error all images in HDD container will be closed.
6902 *
6903 * @returns VBox status code.
6904 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
6905 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
6906 * @param pDisk Pointer to HDD container.
6907 * @param nImage Image number, counts from 0. 0 is always base image of container.
6908 * @param pPCHSGeometry Where to load PCHS geometry from. Not NULL.
6909 */
6910VBOXDDU_DECL(int) VDSetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
6911 PCVDGEOMETRY pPCHSGeometry)
6912{
6913 int rc = VINF_SUCCESS;
6914 int rc2;
6915 bool fLockWrite = false;
6916
6917 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
6918 pDisk, nImage, pPCHSGeometry, pPCHSGeometry->cCylinders,
6919 pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
6920 do
6921 {
6922 /* sanity check */
6923 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6924 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6925
6926 /* Check arguments. */
6927 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
6928 && pPCHSGeometry->cHeads <= 16
6929 && pPCHSGeometry->cSectors <= 63,
6930 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
6931 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
6932 pPCHSGeometry->cSectors),
6933 rc = VERR_INVALID_PARAMETER);
6934
6935 rc2 = vdThreadStartWrite(pDisk);
6936 AssertRC(rc2);
6937 fLockWrite = true;
6938
6939 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6940 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
6941
6942 if (pImage == pDisk->pLast)
6943 {
6944 if ( pPCHSGeometry->cCylinders != pDisk->PCHSGeometry.cCylinders
6945 || pPCHSGeometry->cHeads != pDisk->PCHSGeometry.cHeads
6946 || pPCHSGeometry->cSectors != pDisk->PCHSGeometry.cSectors)
6947 {
6948 /* Only update geometry if it is changed. Avoids similar checks
6949 * in every backend. Most of the time the new geometry is set
6950 * to the previous values, so no need to go through the hassle
6951 * of updating an image which could be opened in read-only mode
6952 * right now. */
6953 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pBackendData,
6954 pPCHSGeometry);
6955
6956 /* Cache new geometry values in any case. */
6957 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
6958 &pDisk->PCHSGeometry);
6959 if (RT_FAILURE(rc2))
6960 {
6961 pDisk->PCHSGeometry.cCylinders = 0;
6962 pDisk->PCHSGeometry.cHeads = 0;
6963 pDisk->PCHSGeometry.cSectors = 0;
6964 }
6965 else
6966 {
6967 /* Make sure the CHS geometry is properly clipped. */
6968 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 255);
6969 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
6970 }
6971 }
6972 }
6973 else
6974 {
6975 VDGEOMETRY PCHS;
6976 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
6977 &PCHS);
6978 if ( RT_FAILURE(rc)
6979 || pPCHSGeometry->cCylinders != PCHS.cCylinders
6980 || pPCHSGeometry->cHeads != PCHS.cHeads
6981 || pPCHSGeometry->cSectors != PCHS.cSectors)
6982 {
6983 /* Only update geometry if it is changed. Avoids similar checks
6984 * in every backend. Most of the time the new geometry is set
6985 * to the previous values, so no need to go through the hassle
6986 * of updating an image which could be opened in read-only mode
6987 * right now. */
6988 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pBackendData,
6989 pPCHSGeometry);
6990 }
6991 }
6992 } while (0);
6993
6994 if (RT_UNLIKELY(fLockWrite))
6995 {
6996 rc2 = vdThreadFinishWrite(pDisk);
6997 AssertRC(rc2);
6998 }
6999
7000 LogFlowFunc(("returns %Rrc\n", rc));
7001 return rc;
7002}
7003
7004/**
7005 * Get virtual disk LCHS geometry stored in HDD container.
7006 *
7007 * @returns VBox status code.
7008 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7009 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
7010 * @param pDisk Pointer to HDD container.
7011 * @param nImage Image number, counts from 0. 0 is always base image of container.
7012 * @param pLCHSGeometry Where to store LCHS geometry. Not NULL.
7013 */
7014VBOXDDU_DECL(int) VDGetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
7015 PVDGEOMETRY pLCHSGeometry)
7016{
7017 int rc = VINF_SUCCESS;
7018 int rc2;
7019 bool fLockRead = false;
7020
7021 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p\n",
7022 pDisk, nImage, pLCHSGeometry));
7023 do
7024 {
7025 /* sanity check */
7026 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7027 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7028
7029 /* Check arguments. */
7030 AssertMsgBreakStmt(VALID_PTR(pLCHSGeometry),
7031 ("pLCHSGeometry=%#p\n", pLCHSGeometry),
7032 rc = VERR_INVALID_PARAMETER);
7033
7034 rc2 = vdThreadStartRead(pDisk);
7035 AssertRC(rc2);
7036 fLockRead = true;
7037
7038 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7039 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7040
7041 if (pImage == pDisk->pLast)
7042 {
7043 /* Use cached information if possible. */
7044 if (pDisk->LCHSGeometry.cCylinders != 0)
7045 *pLCHSGeometry = pDisk->LCHSGeometry;
7046 else
7047 rc = VERR_VD_GEOMETRY_NOT_SET;
7048 }
7049 else
7050 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
7051 pLCHSGeometry);
7052 } while (0);
7053
7054 if (RT_UNLIKELY(fLockRead))
7055 {
7056 rc2 = vdThreadFinishRead(pDisk);
7057 AssertRC(rc2);
7058 }
7059
7060 LogFlowFunc((": %Rrc (LCHS=%u/%u/%u)\n", rc,
7061 pDisk->LCHSGeometry.cCylinders, pDisk->LCHSGeometry.cHeads,
7062 pDisk->LCHSGeometry.cSectors));
7063 return rc;
7064}
7065
7066/**
7067 * Store virtual disk LCHS geometry in HDD container.
7068 *
7069 * Note that in case of unrecoverable error all images in HDD container will be closed.
7070 *
7071 * @returns VBox status code.
7072 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7073 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
7074 * @param pDisk Pointer to HDD container.
7075 * @param nImage Image number, counts from 0. 0 is always base image of container.
7076 * @param pLCHSGeometry Where to load LCHS geometry from. Not NULL.
7077 */
7078VBOXDDU_DECL(int) VDSetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
7079 PCVDGEOMETRY pLCHSGeometry)
7080{
7081 int rc = VINF_SUCCESS;
7082 int rc2;
7083 bool fLockWrite = false;
7084
7085 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
7086 pDisk, nImage, pLCHSGeometry, pLCHSGeometry->cCylinders,
7087 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
7088 do
7089 {
7090 /* sanity check */
7091 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7092 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7093
7094 /* Check arguments. */
7095 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
7096 && pLCHSGeometry->cHeads <= 255
7097 && pLCHSGeometry->cSectors <= 63,
7098 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
7099 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
7100 pLCHSGeometry->cSectors),
7101 rc = VERR_INVALID_PARAMETER);
7102
7103 rc2 = vdThreadStartWrite(pDisk);
7104 AssertRC(rc2);
7105 fLockWrite = true;
7106
7107 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7108 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7109
7110 if (pImage == pDisk->pLast)
7111 {
7112 if ( pLCHSGeometry->cCylinders != pDisk->LCHSGeometry.cCylinders
7113 || pLCHSGeometry->cHeads != pDisk->LCHSGeometry.cHeads
7114 || pLCHSGeometry->cSectors != pDisk->LCHSGeometry.cSectors)
7115 {
7116 /* Only update geometry if it is changed. Avoids similar checks
7117 * in every backend. Most of the time the new geometry is set
7118 * to the previous values, so no need to go through the hassle
7119 * of updating an image which could be opened in read-only mode
7120 * right now. */
7121 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pBackendData,
7122 pLCHSGeometry);
7123
7124 /* Cache new geometry values in any case. */
7125 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
7126 &pDisk->LCHSGeometry);
7127 if (RT_FAILURE(rc2))
7128 {
7129 pDisk->LCHSGeometry.cCylinders = 0;
7130 pDisk->LCHSGeometry.cHeads = 0;
7131 pDisk->LCHSGeometry.cSectors = 0;
7132 }
7133 else
7134 {
7135 /* Make sure the CHS geometry is properly clipped. */
7136 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
7137 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
7138 }
7139 }
7140 }
7141 else
7142 {
7143 VDGEOMETRY LCHS;
7144 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
7145 &LCHS);
7146 if ( RT_FAILURE(rc)
7147 || pLCHSGeometry->cCylinders != LCHS.cCylinders
7148 || pLCHSGeometry->cHeads != LCHS.cHeads
7149 || pLCHSGeometry->cSectors != LCHS.cSectors)
7150 {
7151 /* Only update geometry if it is changed. Avoids similar checks
7152 * in every backend. Most of the time the new geometry is set
7153 * to the previous values, so no need to go through the hassle
7154 * of updating an image which could be opened in read-only mode
7155 * right now. */
7156 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pBackendData,
7157 pLCHSGeometry);
7158 }
7159 }
7160 } while (0);
7161
7162 if (RT_UNLIKELY(fLockWrite))
7163 {
7164 rc2 = vdThreadFinishWrite(pDisk);
7165 AssertRC(rc2);
7166 }
7167
7168 LogFlowFunc(("returns %Rrc\n", rc));
7169 return rc;
7170}
7171
7172/**
7173 * Get version of image in HDD container.
7174 *
7175 * @returns VBox status code.
7176 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7177 * @param pDisk Pointer to HDD container.
7178 * @param nImage Image number, counts from 0. 0 is always base image of container.
7179 * @param puVersion Where to store the image version.
7180 */
7181VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
7182 unsigned *puVersion)
7183{
7184 int rc = VINF_SUCCESS;
7185 int rc2;
7186 bool fLockRead = false;
7187
7188 LogFlowFunc(("pDisk=%#p nImage=%u puVersion=%#p\n",
7189 pDisk, nImage, puVersion));
7190 do
7191 {
7192 /* sanity check */
7193 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7194 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7195
7196 /* Check arguments. */
7197 AssertMsgBreakStmt(VALID_PTR(puVersion),
7198 ("puVersion=%#p\n", puVersion),
7199 rc = VERR_INVALID_PARAMETER);
7200
7201 rc2 = vdThreadStartRead(pDisk);
7202 AssertRC(rc2);
7203 fLockRead = true;
7204
7205 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7206 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7207
7208 *puVersion = pImage->Backend->pfnGetVersion(pImage->pBackendData);
7209 } while (0);
7210
7211 if (RT_UNLIKELY(fLockRead))
7212 {
7213 rc2 = vdThreadFinishRead(pDisk);
7214 AssertRC(rc2);
7215 }
7216
7217 LogFlowFunc(("returns %Rrc uVersion=%#x\n", rc, *puVersion));
7218 return rc;
7219}
7220
7221/**
7222 * List the capabilities of image backend in HDD container.
7223 *
7224 * @returns VBox status code.
7225 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7226 * @param pDisk Pointer to the HDD container.
7227 * @param nImage Image number, counts from 0. 0 is always base image of container.
7228 * @param pbackendInfo Where to store the backend information.
7229 */
7230VBOXDDU_DECL(int) VDBackendInfoSingle(PVBOXHDD pDisk, unsigned nImage,
7231 PVDBACKENDINFO pBackendInfo)
7232{
7233 int rc = VINF_SUCCESS;
7234 int rc2;
7235 bool fLockRead = false;
7236
7237 LogFlowFunc(("pDisk=%#p nImage=%u pBackendInfo=%#p\n",
7238 pDisk, nImage, pBackendInfo));
7239 do
7240 {
7241 /* sanity check */
7242 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7243 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7244
7245 /* Check arguments. */
7246 AssertMsgBreakStmt(VALID_PTR(pBackendInfo),
7247 ("pBackendInfo=%#p\n", pBackendInfo),
7248 rc = VERR_INVALID_PARAMETER);
7249
7250 rc2 = vdThreadStartRead(pDisk);
7251 AssertRC(rc2);
7252 fLockRead = true;
7253
7254 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7255 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7256
7257 pBackendInfo->pszBackend = pImage->Backend->pszBackendName;
7258 pBackendInfo->uBackendCaps = pImage->Backend->uBackendCaps;
7259 pBackendInfo->paFileExtensions = pImage->Backend->paFileExtensions;
7260 pBackendInfo->paConfigInfo = pImage->Backend->paConfigInfo;
7261 } while (0);
7262
7263 if (RT_UNLIKELY(fLockRead))
7264 {
7265 rc2 = vdThreadFinishRead(pDisk);
7266 AssertRC(rc2);
7267 }
7268
7269 LogFlowFunc(("returns %Rrc\n", rc));
7270 return rc;
7271}
7272
7273/**
7274 * Get flags of image in HDD container.
7275 *
7276 * @returns VBox status code.
7277 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7278 * @param pDisk Pointer to HDD container.
7279 * @param nImage Image number, counts from 0. 0 is always base image of container.
7280 * @param puImageFlags Where to store the image flags.
7281 */
7282VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage,
7283 unsigned *puImageFlags)
7284{
7285 int rc = VINF_SUCCESS;
7286 int rc2;
7287 bool fLockRead = false;
7288
7289 LogFlowFunc(("pDisk=%#p nImage=%u puImageFlags=%#p\n",
7290 pDisk, nImage, puImageFlags));
7291 do
7292 {
7293 /* sanity check */
7294 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7295 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7296
7297 /* Check arguments. */
7298 AssertMsgBreakStmt(VALID_PTR(puImageFlags),
7299 ("puImageFlags=%#p\n", puImageFlags),
7300 rc = VERR_INVALID_PARAMETER);
7301
7302 rc2 = vdThreadStartRead(pDisk);
7303 AssertRC(rc2);
7304 fLockRead = true;
7305
7306 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7307 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7308
7309 *puImageFlags = pImage->uImageFlags;
7310 } while (0);
7311
7312 if (RT_UNLIKELY(fLockRead))
7313 {
7314 rc2 = vdThreadFinishRead(pDisk);
7315 AssertRC(rc2);
7316 }
7317
7318 LogFlowFunc(("returns %Rrc uImageFlags=%#x\n", rc, *puImageFlags));
7319 return rc;
7320}
7321
7322/**
7323 * Get open flags of image in HDD container.
7324 *
7325 * @returns VBox status code.
7326 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7327 * @param pDisk Pointer to HDD container.
7328 * @param nImage Image number, counts from 0. 0 is always base image of container.
7329 * @param puOpenFlags Where to store the image open flags.
7330 */
7331VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
7332 unsigned *puOpenFlags)
7333{
7334 int rc = VINF_SUCCESS;
7335 int rc2;
7336 bool fLockRead = false;
7337
7338 LogFlowFunc(("pDisk=%#p nImage=%u puOpenFlags=%#p\n",
7339 pDisk, nImage, puOpenFlags));
7340 do
7341 {
7342 /* sanity check */
7343 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7344 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7345
7346 /* Check arguments. */
7347 AssertMsgBreakStmt(VALID_PTR(puOpenFlags),
7348 ("puOpenFlags=%#p\n", puOpenFlags),
7349 rc = VERR_INVALID_PARAMETER);
7350
7351 rc2 = vdThreadStartRead(pDisk);
7352 AssertRC(rc2);
7353 fLockRead = true;
7354
7355 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7356 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7357
7358 *puOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
7359 } while (0);
7360
7361 if (RT_UNLIKELY(fLockRead))
7362 {
7363 rc2 = vdThreadFinishRead(pDisk);
7364 AssertRC(rc2);
7365 }
7366
7367 LogFlowFunc(("returns %Rrc uOpenFlags=%#x\n", rc, *puOpenFlags));
7368 return rc;
7369}
7370
7371/**
7372 * Set open flags of image in HDD container.
7373 * This operation may cause file locking changes and/or files being reopened.
7374 * Note that in case of unrecoverable error all images in HDD container will be closed.
7375 *
7376 * @returns VBox status code.
7377 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7378 * @param pDisk Pointer to HDD container.
7379 * @param nImage Image number, counts from 0. 0 is always base image of container.
7380 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
7381 */
7382VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
7383 unsigned uOpenFlags)
7384{
7385 int rc;
7386 int rc2;
7387 bool fLockWrite = false;
7388
7389 LogFlowFunc(("pDisk=%#p uOpenFlags=%#u\n", pDisk, uOpenFlags));
7390 do
7391 {
7392 /* sanity check */
7393 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7394 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7395
7396 /* Check arguments. */
7397 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
7398 ("uOpenFlags=%#x\n", uOpenFlags),
7399 rc = VERR_INVALID_PARAMETER);
7400
7401 rc2 = vdThreadStartWrite(pDisk);
7402 AssertRC(rc2);
7403 fLockWrite = true;
7404
7405 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7406 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7407
7408 rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData,
7409 uOpenFlags);
7410 } while (0);
7411
7412 if (RT_UNLIKELY(fLockWrite))
7413 {
7414 rc2 = vdThreadFinishWrite(pDisk);
7415 AssertRC(rc2);
7416 }
7417
7418 LogFlowFunc(("returns %Rrc\n", rc));
7419 return rc;
7420}
7421
7422/**
7423 * Get base filename of image in HDD container. Some image formats use
7424 * other filenames as well, so don't use this for anything but informational
7425 * purposes.
7426 *
7427 * @returns VBox status code.
7428 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7429 * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
7430 * @param pDisk Pointer to HDD container.
7431 * @param nImage Image number, counts from 0. 0 is always base image of container.
7432 * @param pszFilename Where to store the image file name.
7433 * @param cbFilename Size of buffer pszFilename points to.
7434 */
7435VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
7436 char *pszFilename, unsigned cbFilename)
7437{
7438 int rc;
7439 int rc2;
7440 bool fLockRead = false;
7441
7442 LogFlowFunc(("pDisk=%#p nImage=%u pszFilename=%#p cbFilename=%u\n",
7443 pDisk, nImage, pszFilename, cbFilename));
7444 do
7445 {
7446 /* sanity check */
7447 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7448 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7449
7450 /* Check arguments. */
7451 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
7452 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
7453 rc = VERR_INVALID_PARAMETER);
7454 AssertMsgBreakStmt(cbFilename,
7455 ("cbFilename=%u\n", cbFilename),
7456 rc = VERR_INVALID_PARAMETER);
7457
7458 rc2 = vdThreadStartRead(pDisk);
7459 AssertRC(rc2);
7460 fLockRead = true;
7461
7462 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7463 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7464
7465 size_t cb = strlen(pImage->pszFilename);
7466 if (cb <= cbFilename)
7467 {
7468 strcpy(pszFilename, pImage->pszFilename);
7469 rc = VINF_SUCCESS;
7470 }
7471 else
7472 {
7473 strncpy(pszFilename, pImage->pszFilename, cbFilename - 1);
7474 pszFilename[cbFilename - 1] = '\0';
7475 rc = VERR_BUFFER_OVERFLOW;
7476 }
7477 } while (0);
7478
7479 if (RT_UNLIKELY(fLockRead))
7480 {
7481 rc2 = vdThreadFinishRead(pDisk);
7482 AssertRC(rc2);
7483 }
7484
7485 LogFlowFunc(("returns %Rrc, pszFilename=\"%s\"\n", rc, pszFilename));
7486 return rc;
7487}
7488
7489/**
7490 * Get the comment line of image in HDD container.
7491 *
7492 * @returns VBox status code.
7493 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7494 * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
7495 * @param pDisk Pointer to HDD container.
7496 * @param nImage Image number, counts from 0. 0 is always base image of container.
7497 * @param pszComment Where to store the comment string of image. NULL is ok.
7498 * @param cbComment The size of pszComment buffer. 0 is ok.
7499 */
7500VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
7501 char *pszComment, unsigned cbComment)
7502{
7503 int rc;
7504 int rc2;
7505 bool fLockRead = false;
7506
7507 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p cbComment=%u\n",
7508 pDisk, nImage, pszComment, cbComment));
7509 do
7510 {
7511 /* sanity check */
7512 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7513 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7514
7515 /* Check arguments. */
7516 AssertMsgBreakStmt(VALID_PTR(pszComment),
7517 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
7518 rc = VERR_INVALID_PARAMETER);
7519 AssertMsgBreakStmt(cbComment,
7520 ("cbComment=%u\n", cbComment),
7521 rc = VERR_INVALID_PARAMETER);
7522
7523 rc2 = vdThreadStartRead(pDisk);
7524 AssertRC(rc2);
7525 fLockRead = true;
7526
7527 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7528 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7529
7530 rc = pImage->Backend->pfnGetComment(pImage->pBackendData, pszComment,
7531 cbComment);
7532 } while (0);
7533
7534 if (RT_UNLIKELY(fLockRead))
7535 {
7536 rc2 = vdThreadFinishRead(pDisk);
7537 AssertRC(rc2);
7538 }
7539
7540 LogFlowFunc(("returns %Rrc, pszComment=\"%s\"\n", rc, pszComment));
7541 return rc;
7542}
7543
7544/**
7545 * Changes the comment line of image in HDD container.
7546 *
7547 * @returns VBox status code.
7548 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7549 * @param pDisk Pointer to HDD container.
7550 * @param nImage Image number, counts from 0. 0 is always base image of container.
7551 * @param pszComment New comment string (UTF-8). NULL is allowed to reset the comment.
7552 */
7553VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
7554 const char *pszComment)
7555{
7556 int rc;
7557 int rc2;
7558 bool fLockWrite = false;
7559
7560 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p \"%s\"\n",
7561 pDisk, nImage, pszComment, pszComment));
7562 do
7563 {
7564 /* sanity check */
7565 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7566 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7567
7568 /* Check arguments. */
7569 AssertMsgBreakStmt(VALID_PTR(pszComment) || pszComment == NULL,
7570 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
7571 rc = VERR_INVALID_PARAMETER);
7572
7573 rc2 = vdThreadStartWrite(pDisk);
7574 AssertRC(rc2);
7575 fLockWrite = true;
7576
7577 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7578 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7579
7580 rc = pImage->Backend->pfnSetComment(pImage->pBackendData, pszComment);
7581 } while (0);
7582
7583 if (RT_UNLIKELY(fLockWrite))
7584 {
7585 rc2 = vdThreadFinishWrite(pDisk);
7586 AssertRC(rc2);
7587 }
7588
7589 LogFlowFunc(("returns %Rrc\n", rc));
7590 return rc;
7591}
7592
7593
7594/**
7595 * Get UUID of image in HDD container.
7596 *
7597 * @returns VBox status code.
7598 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7599 * @param pDisk Pointer to HDD container.
7600 * @param nImage Image number, counts from 0. 0 is always base image of container.
7601 * @param pUuid Where to store the image creation UUID.
7602 */
7603VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
7604{
7605 int rc;
7606 int rc2;
7607 bool fLockRead = false;
7608
7609 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
7610 do
7611 {
7612 /* sanity check */
7613 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7614 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7615
7616 /* Check arguments. */
7617 AssertMsgBreakStmt(VALID_PTR(pUuid),
7618 ("pUuid=%#p\n", pUuid),
7619 rc = VERR_INVALID_PARAMETER);
7620
7621 rc2 = vdThreadStartRead(pDisk);
7622 AssertRC(rc2);
7623 fLockRead = true;
7624
7625 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7626 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7627
7628 rc = pImage->Backend->pfnGetUuid(pImage->pBackendData, pUuid);
7629 } while (0);
7630
7631 if (RT_UNLIKELY(fLockRead))
7632 {
7633 rc2 = vdThreadFinishRead(pDisk);
7634 AssertRC(rc2);
7635 }
7636
7637 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
7638 return rc;
7639}
7640
7641/**
7642 * Set the image's UUID. Should not be used by normal applications.
7643 *
7644 * @returns VBox status code.
7645 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7646 * @param pDisk Pointer to HDD container.
7647 * @param nImage Image number, counts from 0. 0 is always base image of container.
7648 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
7649 */
7650VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
7651{
7652 int rc;
7653 int rc2;
7654 bool fLockWrite = false;
7655
7656 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
7657 pDisk, nImage, pUuid, pUuid));
7658 do
7659 {
7660 /* sanity check */
7661 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7662 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7663
7664 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
7665 ("pUuid=%#p\n", pUuid),
7666 rc = VERR_INVALID_PARAMETER);
7667
7668 rc2 = vdThreadStartWrite(pDisk);
7669 AssertRC(rc2);
7670 fLockWrite = true;
7671
7672 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7673 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7674
7675 RTUUID Uuid;
7676 if (!pUuid)
7677 {
7678 RTUuidCreate(&Uuid);
7679 pUuid = &Uuid;
7680 }
7681 rc = pImage->Backend->pfnSetUuid(pImage->pBackendData, pUuid);
7682 } while (0);
7683
7684 if (RT_UNLIKELY(fLockWrite))
7685 {
7686 rc2 = vdThreadFinishWrite(pDisk);
7687 AssertRC(rc2);
7688 }
7689
7690 LogFlowFunc(("returns %Rrc\n", rc));
7691 return rc;
7692}
7693
7694/**
7695 * Get last modification UUID of image in HDD container.
7696 *
7697 * @returns VBox status code.
7698 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7699 * @param pDisk Pointer to HDD container.
7700 * @param nImage Image number, counts from 0. 0 is always base image of container.
7701 * @param pUuid Where to store the image modification UUID.
7702 */
7703VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
7704{
7705 int rc = VINF_SUCCESS;
7706 int rc2;
7707 bool fLockRead = false;
7708
7709 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
7710 do
7711 {
7712 /* sanity check */
7713 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7714 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7715
7716 /* Check arguments. */
7717 AssertMsgBreakStmt(VALID_PTR(pUuid),
7718 ("pUuid=%#p\n", pUuid),
7719 rc = VERR_INVALID_PARAMETER);
7720
7721 rc2 = vdThreadStartRead(pDisk);
7722 AssertRC(rc2);
7723 fLockRead = true;
7724
7725 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7726 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7727
7728 rc = pImage->Backend->pfnGetModificationUuid(pImage->pBackendData,
7729 pUuid);
7730 } while (0);
7731
7732 if (RT_UNLIKELY(fLockRead))
7733 {
7734 rc2 = vdThreadFinishRead(pDisk);
7735 AssertRC(rc2);
7736 }
7737
7738 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
7739 return rc;
7740}
7741
7742/**
7743 * Set the image's last modification UUID. Should not be used by normal applications.
7744 *
7745 * @returns VBox status code.
7746 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7747 * @param pDisk Pointer to HDD container.
7748 * @param nImage Image number, counts from 0. 0 is always base image of container.
7749 * @param pUuid New modification UUID of the image. If NULL, a new UUID is created.
7750 */
7751VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
7752{
7753 int rc;
7754 int rc2;
7755 bool fLockWrite = false;
7756
7757 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
7758 pDisk, nImage, pUuid, pUuid));
7759 do
7760 {
7761 /* sanity check */
7762 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7763 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7764
7765 /* Check arguments. */
7766 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
7767 ("pUuid=%#p\n", pUuid),
7768 rc = VERR_INVALID_PARAMETER);
7769
7770 rc2 = vdThreadStartWrite(pDisk);
7771 AssertRC(rc2);
7772 fLockWrite = true;
7773
7774 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7775 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7776
7777 RTUUID Uuid;
7778 if (!pUuid)
7779 {
7780 RTUuidCreate(&Uuid);
7781 pUuid = &Uuid;
7782 }
7783 rc = pImage->Backend->pfnSetModificationUuid(pImage->pBackendData,
7784 pUuid);
7785 } while (0);
7786
7787 if (RT_UNLIKELY(fLockWrite))
7788 {
7789 rc2 = vdThreadFinishWrite(pDisk);
7790 AssertRC(rc2);
7791 }
7792
7793 LogFlowFunc(("returns %Rrc\n", rc));
7794 return rc;
7795}
7796
7797/**
7798 * Get parent UUID of image in HDD container.
7799 *
7800 * @returns VBox status code.
7801 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7802 * @param pDisk Pointer to HDD container.
7803 * @param nImage Image number, counts from 0. 0 is always base image of container.
7804 * @param pUuid Where to store the parent image UUID.
7805 */
7806VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
7807 PRTUUID pUuid)
7808{
7809 int rc = VINF_SUCCESS;
7810 int rc2;
7811 bool fLockRead = false;
7812
7813 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
7814 do
7815 {
7816 /* sanity check */
7817 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7818 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7819
7820 /* Check arguments. */
7821 AssertMsgBreakStmt(VALID_PTR(pUuid),
7822 ("pUuid=%#p\n", pUuid),
7823 rc = VERR_INVALID_PARAMETER);
7824
7825 rc2 = vdThreadStartRead(pDisk);
7826 AssertRC(rc2);
7827 fLockRead = true;
7828
7829 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7830 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7831
7832 rc = pImage->Backend->pfnGetParentUuid(pImage->pBackendData, pUuid);
7833 } while (0);
7834
7835 if (RT_UNLIKELY(fLockRead))
7836 {
7837 rc2 = vdThreadFinishRead(pDisk);
7838 AssertRC(rc2);
7839 }
7840
7841 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
7842 return rc;
7843}
7844
7845/**
7846 * Set the image's parent UUID. Should not be used by normal applications.
7847 *
7848 * @returns VBox status code.
7849 * @param pDisk Pointer to HDD container.
7850 * @param nImage Image number, counts from 0. 0 is always base image of container.
7851 * @param pUuid New parent UUID of the image. If NULL, a new UUID is created.
7852 */
7853VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
7854 PCRTUUID pUuid)
7855{
7856 int rc;
7857 int rc2;
7858 bool fLockWrite = false;
7859
7860 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
7861 pDisk, nImage, pUuid, pUuid));
7862 do
7863 {
7864 /* sanity check */
7865 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7866 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7867
7868 /* Check arguments. */
7869 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
7870 ("pUuid=%#p\n", pUuid),
7871 rc = VERR_INVALID_PARAMETER);
7872
7873 rc2 = vdThreadStartWrite(pDisk);
7874 AssertRC(rc2);
7875 fLockWrite = true;
7876
7877 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7878 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7879
7880 RTUUID Uuid;
7881 if (!pUuid)
7882 {
7883 RTUuidCreate(&Uuid);
7884 pUuid = &Uuid;
7885 }
7886 rc = pImage->Backend->pfnSetParentUuid(pImage->pBackendData, pUuid);
7887 } while (0);
7888
7889 if (RT_UNLIKELY(fLockWrite))
7890 {
7891 rc2 = vdThreadFinishWrite(pDisk);
7892 AssertRC(rc2);
7893 }
7894
7895 LogFlowFunc(("returns %Rrc\n", rc));
7896 return rc;
7897}
7898
7899
7900/**
7901 * Debug helper - dumps all opened images in HDD container into the log file.
7902 *
7903 * @param pDisk Pointer to HDD container.
7904 */
7905VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk)
7906{
7907 int rc2;
7908 bool fLockRead = false;
7909
7910 do
7911 {
7912 /* sanity check */
7913 AssertPtrBreak(pDisk);
7914 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7915
7916 if (!pDisk->pInterfaceErrorCallbacks || !VALID_PTR(pDisk->pInterfaceErrorCallbacks->pfnMessage))
7917 pDisk->pInterfaceErrorCallbacks->pfnMessage = vdLogMessage;
7918
7919 rc2 = vdThreadStartRead(pDisk);
7920 AssertRC(rc2);
7921 fLockRead = true;
7922
7923 vdMessageWrapper(pDisk, "--- Dumping VD Disk, Images=%u\n", pDisk->cImages);
7924 for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
7925 {
7926 vdMessageWrapper(pDisk, "Dumping VD image \"%s\" (Backend=%s)\n",
7927 pImage->pszFilename, pImage->Backend->pszBackendName);
7928 pImage->Backend->pfnDump(pImage->pBackendData);
7929 }
7930 } while (0);
7931
7932 if (RT_UNLIKELY(fLockRead))
7933 {
7934 rc2 = vdThreadFinishRead(pDisk);
7935 AssertRC(rc2);
7936 }
7937}
7938
7939/**
7940 * Query if asynchronous operations are supported for this disk.
7941 *
7942 * @returns VBox status code.
7943 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7944 * @param pDisk Pointer to the HDD container.
7945 * @param nImage Image number, counts from 0. 0 is always base image of container.
7946 * @param pfAIOSupported Where to store if async IO is supported.
7947 */
7948VBOXDDU_DECL(int) VDImageIsAsyncIOSupported(PVBOXHDD pDisk, unsigned nImage, bool *pfAIOSupported)
7949{
7950 int rc = VINF_SUCCESS;
7951 int rc2;
7952 bool fLockRead = false;
7953
7954 LogFlowFunc(("pDisk=%#p nImage=%u pfAIOSupported=%#p\n", pDisk, nImage, pfAIOSupported));
7955 do
7956 {
7957 /* sanity check */
7958 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7959 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7960
7961 /* Check arguments. */
7962 AssertMsgBreakStmt(VALID_PTR(pfAIOSupported),
7963 ("pfAIOSupported=%#p\n", pfAIOSupported),
7964 rc = VERR_INVALID_PARAMETER);
7965
7966 rc2 = vdThreadStartRead(pDisk);
7967 AssertRC(rc2);
7968 fLockRead = true;
7969
7970 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7971 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7972
7973 if (pImage->Backend->uBackendCaps & VD_CAP_ASYNC)
7974 *pfAIOSupported = pImage->Backend->pfnIsAsyncIOSupported(pImage->pBackendData);
7975 else
7976 *pfAIOSupported = false;
7977 } while (0);
7978
7979 if (RT_UNLIKELY(fLockRead))
7980 {
7981 rc2 = vdThreadFinishRead(pDisk);
7982 AssertRC(rc2);
7983 }
7984
7985 LogFlowFunc(("returns %Rrc, fAIOSupported=%u\n", rc, *pfAIOSupported));
7986 return rc;
7987}
7988
7989
7990VBOXDDU_DECL(int) VDAsyncRead(PVBOXHDD pDisk, uint64_t uOffset, size_t cbRead,
7991 PCRTSGBUF pcSgBuf,
7992 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
7993 void *pvUser1, void *pvUser2)
7994{
7995 int rc = VERR_VD_BLOCK_FREE;
7996 int rc2;
7997 bool fLockRead = false;
7998 PVDIOCTX pIoCtx = NULL;
7999
8000 LogFlowFunc(("pDisk=%#p uOffset=%llu pcSgBuf=%#p cbRead=%zu pvUser1=%#p pvUser2=%#p\n",
8001 pDisk, uOffset, pcSgBuf, cbRead, pvUser1, pvUser2));
8002
8003 do
8004 {
8005 /* sanity check */
8006 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8007 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8008
8009 /* Check arguments. */
8010 AssertMsgBreakStmt(cbRead,
8011 ("cbRead=%zu\n", cbRead),
8012 rc = VERR_INVALID_PARAMETER);
8013 AssertMsgBreakStmt(VALID_PTR(pcSgBuf),
8014 ("pcSgBuf=%#p\n", pcSgBuf),
8015 rc = VERR_INVALID_PARAMETER);
8016
8017 rc2 = vdThreadStartRead(pDisk);
8018 AssertRC(rc2);
8019 fLockRead = true;
8020
8021 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
8022 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
8023 uOffset, cbRead, pDisk->cbSize),
8024 rc = VERR_INVALID_PARAMETER);
8025 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
8026
8027 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_READ, uOffset,
8028 cbRead, pDisk->pLast, pcSgBuf,
8029 pfnComplete, pvUser1, pvUser2,
8030 NULL, vdReadHelperAsync);
8031 if (!pIoCtx)
8032 {
8033 rc = VERR_NO_MEMORY;
8034 break;
8035 }
8036
8037 rc = vdIoCtxProcess(pIoCtx);
8038 if (rc == VINF_VD_ASYNC_IO_FINISHED)
8039 {
8040 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
8041 vdIoCtxFree(pDisk, pIoCtx);
8042 else
8043 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
8044 }
8045 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
8046 vdIoCtxFree(pDisk, pIoCtx);
8047
8048 } while (0);
8049
8050 if (RT_UNLIKELY(fLockRead) && ( rc == VINF_VD_ASYNC_IO_FINISHED
8051 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
8052 {
8053 rc2 = vdThreadFinishRead(pDisk);
8054 AssertRC(rc2);
8055 }
8056
8057 LogFlowFunc(("returns %Rrc\n", rc));
8058 return rc;
8059}
8060
8061
8062VBOXDDU_DECL(int) VDAsyncWrite(PVBOXHDD pDisk, uint64_t uOffset, size_t cbWrite,
8063 PCRTSGBUF pcSgBuf,
8064 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
8065 void *pvUser1, void *pvUser2)
8066{
8067 int rc;
8068 int rc2;
8069 bool fLockWrite = false;
8070 PVDIOCTX pIoCtx = NULL;
8071
8072 LogFlowFunc(("pDisk=%#p uOffset=%llu cSgBuf=%#p cbWrite=%zu pvUser1=%#p pvUser2=%#p\n",
8073 pDisk, uOffset, pcSgBuf, cbWrite, pvUser1, pvUser2));
8074 do
8075 {
8076 /* sanity check */
8077 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8078 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8079
8080 /* Check arguments. */
8081 AssertMsgBreakStmt(cbWrite,
8082 ("cbWrite=%zu\n", cbWrite),
8083 rc = VERR_INVALID_PARAMETER);
8084 AssertMsgBreakStmt(VALID_PTR(pcSgBuf),
8085 ("pcSgBuf=%#p\n", pcSgBuf),
8086 rc = VERR_INVALID_PARAMETER);
8087
8088 rc2 = vdThreadStartWrite(pDisk);
8089 AssertRC(rc2);
8090 fLockWrite = true;
8091
8092 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
8093 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
8094 uOffset, cbWrite, pDisk->cbSize),
8095 rc = VERR_INVALID_PARAMETER);
8096 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
8097
8098 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_WRITE, uOffset,
8099 cbWrite, pDisk->pLast, pcSgBuf,
8100 pfnComplete, pvUser1, pvUser2,
8101 NULL, vdWriteHelperAsync);
8102 if (!pIoCtx)
8103 {
8104 rc = VERR_NO_MEMORY;
8105 break;
8106 }
8107
8108 rc = vdIoCtxProcess(pIoCtx);
8109 if (rc == VINF_VD_ASYNC_IO_FINISHED)
8110 {
8111 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
8112 vdIoCtxFree(pDisk, pIoCtx);
8113 else
8114 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
8115 }
8116 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
8117 vdIoCtxFree(pDisk, pIoCtx);
8118 } while (0);
8119
8120 if (RT_UNLIKELY(fLockWrite) && ( rc == VINF_VD_ASYNC_IO_FINISHED
8121 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
8122 {
8123 rc2 = vdThreadFinishWrite(pDisk);
8124 AssertRC(rc2);
8125 }
8126
8127 LogFlowFunc(("returns %Rrc\n", rc));
8128 return rc;
8129}
8130
8131
8132VBOXDDU_DECL(int) VDAsyncFlush(PVBOXHDD pDisk, PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
8133 void *pvUser1, void *pvUser2)
8134{
8135 int rc;
8136 int rc2;
8137 bool fLockWrite = false;
8138 PVDIOCTX pIoCtx = NULL;
8139
8140 LogFlowFunc(("pDisk=%#p\n", pDisk));
8141
8142 do
8143 {
8144 /* sanity check */
8145 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8146 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8147
8148 rc2 = vdThreadStartWrite(pDisk);
8149 AssertRC(rc2);
8150 fLockWrite = true;
8151
8152 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
8153
8154 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_FLUSH, 0,
8155 0, pDisk->pLast, NULL,
8156 pfnComplete, pvUser1, pvUser2,
8157 NULL, vdFlushHelperAsync);
8158 if (!pIoCtx)
8159 {
8160 rc = VERR_NO_MEMORY;
8161 break;
8162 }
8163
8164 rc = vdIoCtxProcess(pIoCtx);
8165 if (rc == VINF_VD_ASYNC_IO_FINISHED)
8166 {
8167 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
8168 vdIoCtxFree(pDisk, pIoCtx);
8169 else
8170 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
8171 }
8172 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
8173 vdIoCtxFree(pDisk, pIoCtx);
8174 } while (0);
8175
8176 if (RT_UNLIKELY(fLockWrite) && ( rc == VINF_VD_ASYNC_IO_FINISHED
8177 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
8178 {
8179 rc2 = vdThreadFinishWrite(pDisk);
8180 AssertRC(rc2);
8181 }
8182
8183 LogFlowFunc(("returns %Rrc\n", rc));
8184 return rc;
8185}
8186
Note: See TracBrowser for help on using the repository browser.

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