VirtualBox

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

Last change on this file since 62738 was 62738, checked in by vboxsync, 8 years ago

Storage: warnings.

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