VirtualBox

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

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

Storage: Start of a VHDX backend for readonly access to import those images into VBox, work in progress

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