VirtualBox

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

Last change on this file since 38621 was 38621, checked in by vboxsync, 13 years ago

VD: Initial support to discard unused blocks in an image + support for VDI images

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