VirtualBox

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

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

VD/Discard: Bugfixes

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