VirtualBox

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

Last change on this file since 62739 was 62739, 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 62739 2016-07-30 12:24:45Z 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
5900 LogFlowFunc(("cEntriesAlloc=%u pEntries=%#p pcEntriesUsed=%#p\n", cEntriesAlloc, pEntries, pcEntriesUsed));
5901 /* Check arguments. */
5902 AssertMsgReturn(cEntriesAlloc,
5903 ("cEntriesAlloc=%u\n", cEntriesAlloc),
5904 VERR_INVALID_PARAMETER);
5905 AssertMsgReturn(VALID_PTR(pEntries),
5906 ("pEntries=%#p\n", pEntries),
5907 VERR_INVALID_PARAMETER);
5908 AssertMsgReturn(VALID_PTR(pcEntriesUsed),
5909 ("pcEntriesUsed=%#p\n", pcEntriesUsed),
5910 VERR_INVALID_PARAMETER);
5911 if (!g_apBackends)
5912 VDInit();
5913
5914 if (cEntriesAlloc < g_cBackends)
5915 {
5916 *pcEntriesUsed = g_cBackends;
5917 return VERR_BUFFER_OVERFLOW;
5918 }
5919
5920 for (unsigned i = 0; i < g_cBackends; i++)
5921 {
5922 pEntries[i].pszBackend = g_apBackends[i]->pszBackendName;
5923 pEntries[i].uBackendCaps = g_apBackends[i]->uBackendCaps;
5924 pEntries[i].paFileExtensions = g_apBackends[i]->paFileExtensions;
5925 pEntries[i].paConfigInfo = g_apBackends[i]->paConfigInfo;
5926 pEntries[i].pfnComposeLocation = g_apBackends[i]->pfnComposeLocation;
5927 pEntries[i].pfnComposeName = g_apBackends[i]->pfnComposeName;
5928 }
5929
5930 LogFlowFunc(("returns %Rrc *pcEntriesUsed=%u\n", rc, g_cBackends));
5931 *pcEntriesUsed = g_cBackends;
5932 return rc;
5933}
5934
5935/**
5936 * Lists the capabilities of a backend identified by its name.
5937 *
5938 * @returns VBox status code.
5939 * @param pszBackend The backend name.
5940 * @param pEntries Pointer to an entry.
5941 */
5942VBOXDDU_DECL(int) VDBackendInfoOne(const char *pszBackend, PVDBACKENDINFO pEntry)
5943{
5944 LogFlowFunc(("pszBackend=%#p pEntry=%#p\n", pszBackend, pEntry));
5945 /* Check arguments. */
5946 AssertMsgReturn(VALID_PTR(pszBackend),
5947 ("pszBackend=%#p\n", pszBackend),
5948 VERR_INVALID_PARAMETER);
5949 AssertMsgReturn(VALID_PTR(pEntry),
5950 ("pEntry=%#p\n", pEntry),
5951 VERR_INVALID_PARAMETER);
5952 if (!g_apBackends)
5953 VDInit();
5954
5955 /* Go through loaded backends. */
5956 for (unsigned i = 0; i < g_cBackends; i++)
5957 {
5958 if (!RTStrICmp(pszBackend, g_apBackends[i]->pszBackendName))
5959 {
5960 pEntry->pszBackend = g_apBackends[i]->pszBackendName;
5961 pEntry->uBackendCaps = g_apBackends[i]->uBackendCaps;
5962 pEntry->paFileExtensions = g_apBackends[i]->paFileExtensions;
5963 pEntry->paConfigInfo = g_apBackends[i]->paConfigInfo;
5964 return VINF_SUCCESS;
5965 }
5966 }
5967
5968 return VERR_NOT_FOUND;
5969}
5970
5971/**
5972 * Lists all filters and their capabilities in a caller-provided buffer.
5973 *
5974 * @return VBox status code.
5975 * VERR_BUFFER_OVERFLOW if not enough space is passed.
5976 * @param cEntriesAlloc Number of list entries available.
5977 * @param pEntries Pointer to array for the entries.
5978 * @param pcEntriesUsed Number of entries returned.
5979 */
5980VBOXDDU_DECL(int) VDFilterInfo(unsigned cEntriesAlloc, PVDFILTERINFO pEntries,
5981 unsigned *pcEntriesUsed)
5982{
5983 int rc = VINF_SUCCESS;
5984
5985 LogFlowFunc(("cEntriesAlloc=%u pEntries=%#p pcEntriesUsed=%#p\n", cEntriesAlloc, pEntries, pcEntriesUsed));
5986 /* Check arguments. */
5987 AssertMsgReturn(cEntriesAlloc,
5988 ("cEntriesAlloc=%u\n", cEntriesAlloc),
5989 VERR_INVALID_PARAMETER);
5990 AssertMsgReturn(VALID_PTR(pEntries),
5991 ("pEntries=%#p\n", pEntries),
5992 VERR_INVALID_PARAMETER);
5993 AssertMsgReturn(VALID_PTR(pcEntriesUsed),
5994 ("pcEntriesUsed=%#p\n", pcEntriesUsed),
5995 VERR_INVALID_PARAMETER);
5996 if (!g_apBackends)
5997 VDInit();
5998
5999 if (cEntriesAlloc < g_cFilterBackends)
6000 {
6001 *pcEntriesUsed = g_cFilterBackends;
6002 return VERR_BUFFER_OVERFLOW;
6003 }
6004
6005 for (unsigned i = 0; i < g_cFilterBackends; i++)
6006 {
6007 pEntries[i].pszFilter = g_apFilterBackends[i]->pszBackendName;
6008 pEntries[i].paConfigInfo = g_apFilterBackends[i]->paConfigInfo;
6009 }
6010
6011 LogFlowFunc(("returns %Rrc *pcEntriesUsed=%u\n", rc, g_cFilterBackends));
6012 *pcEntriesUsed = g_cFilterBackends;
6013 return rc;
6014}
6015
6016/**
6017 * Lists the capabilities of a filter identified by its name.
6018 *
6019 * @return VBox status code.
6020 * @param pszFilter The filter name (case insensitive).
6021 * @param pEntries Pointer to an entry.
6022 */
6023VBOXDDU_DECL(int) VDFilterInfoOne(const char *pszFilter, PVDFILTERINFO pEntry)
6024{
6025 LogFlowFunc(("pszFilter=%#p pEntry=%#p\n", pszFilter, pEntry));
6026 /* Check arguments. */
6027 AssertMsgReturn(VALID_PTR(pszFilter),
6028 ("pszFilter=%#p\n", pszFilter),
6029 VERR_INVALID_PARAMETER);
6030 AssertMsgReturn(VALID_PTR(pEntry),
6031 ("pEntry=%#p\n", pEntry),
6032 VERR_INVALID_PARAMETER);
6033 if (!g_apBackends)
6034 VDInit();
6035
6036 /* Go through loaded backends. */
6037 for (unsigned i = 0; i < g_cFilterBackends; i++)
6038 {
6039 if (!RTStrICmp(pszFilter, g_apFilterBackends[i]->pszBackendName))
6040 {
6041 pEntry->pszFilter = g_apFilterBackends[i]->pszBackendName;
6042 pEntry->paConfigInfo = g_apFilterBackends[i]->paConfigInfo;
6043 return VINF_SUCCESS;
6044 }
6045 }
6046
6047 return VERR_NOT_FOUND;
6048}
6049
6050/**
6051 * Allocates and initializes an empty HDD container.
6052 * No image files are opened.
6053 *
6054 * @returns VBox status code.
6055 * @param pVDIfsDisk Pointer to the per-disk VD interface list.
6056 * @param enmType Type of the image container.
6057 * @param ppDisk Where to store the reference to HDD container.
6058 */
6059VBOXDDU_DECL(int) VDCreate(PVDINTERFACE pVDIfsDisk, VDTYPE enmType, PVBOXHDD *ppDisk)
6060{
6061 int rc = VINF_SUCCESS;
6062 PVBOXHDD pDisk = NULL;
6063
6064 LogFlowFunc(("pVDIfsDisk=%#p\n", pVDIfsDisk));
6065 do
6066 {
6067 /* Check arguments. */
6068 AssertMsgBreakStmt(VALID_PTR(ppDisk),
6069 ("ppDisk=%#p\n", ppDisk),
6070 rc = VERR_INVALID_PARAMETER);
6071
6072 pDisk = (PVBOXHDD)RTMemAllocZ(sizeof(VBOXHDD));
6073 if (pDisk)
6074 {
6075 pDisk->u32Signature = VBOXHDDDISK_SIGNATURE;
6076 pDisk->enmType = enmType;
6077 pDisk->cImages = 0;
6078 pDisk->pBase = NULL;
6079 pDisk->pLast = NULL;
6080 pDisk->cbSize = 0;
6081 pDisk->PCHSGeometry.cCylinders = 0;
6082 pDisk->PCHSGeometry.cHeads = 0;
6083 pDisk->PCHSGeometry.cSectors = 0;
6084 pDisk->LCHSGeometry.cCylinders = 0;
6085 pDisk->LCHSGeometry.cHeads = 0;
6086 pDisk->LCHSGeometry.cSectors = 0;
6087 pDisk->pVDIfsDisk = pVDIfsDisk;
6088 pDisk->pInterfaceError = NULL;
6089 pDisk->pInterfaceThreadSync = NULL;
6090 pDisk->pIoCtxLockOwner = NULL;
6091 pDisk->pIoCtxHead = NULL;
6092 pDisk->fLocked = false;
6093 pDisk->hMemCacheIoCtx = NIL_RTMEMCACHE;
6094 pDisk->hMemCacheIoTask = NIL_RTMEMCACHE;
6095 RTListInit(&pDisk->ListFilterChainWrite);
6096 RTListInit(&pDisk->ListFilterChainRead);
6097
6098 /* Create the I/O ctx cache */
6099 rc = RTMemCacheCreate(&pDisk->hMemCacheIoCtx, sizeof(VDIOCTX), 0, UINT32_MAX,
6100 NULL, NULL, NULL, 0);
6101 if (RT_FAILURE(rc))
6102 break;
6103
6104 /* Create the I/O task cache */
6105 rc = RTMemCacheCreate(&pDisk->hMemCacheIoTask, sizeof(VDIOTASK), 0, UINT32_MAX,
6106 NULL, NULL, NULL, 0);
6107 if (RT_FAILURE(rc))
6108 break;
6109
6110 pDisk->pInterfaceError = VDIfErrorGet(pVDIfsDisk);
6111 pDisk->pInterfaceThreadSync = VDIfThreadSyncGet(pVDIfsDisk);
6112
6113 *ppDisk = pDisk;
6114 }
6115 else
6116 {
6117 rc = VERR_NO_MEMORY;
6118 break;
6119 }
6120 } while (0);
6121
6122 if ( RT_FAILURE(rc)
6123 && pDisk)
6124 {
6125 if (pDisk->hMemCacheIoCtx != NIL_RTMEMCACHE)
6126 RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
6127 if (pDisk->hMemCacheIoTask != NIL_RTMEMCACHE)
6128 RTMemCacheDestroy(pDisk->hMemCacheIoTask);
6129 }
6130
6131 LogFlowFunc(("returns %Rrc (pDisk=%#p)\n", rc, pDisk));
6132 return rc;
6133}
6134
6135/**
6136 * Destroys HDD container.
6137 * If container has opened image files they will be closed.
6138 *
6139 * @returns VBox status code.
6140 * @param pDisk Pointer to HDD container.
6141 */
6142VBOXDDU_DECL(int) VDDestroy(PVBOXHDD pDisk)
6143{
6144 int rc = VINF_SUCCESS;
6145 LogFlowFunc(("pDisk=%#p\n", pDisk));
6146 do
6147 {
6148 /* sanity check */
6149 AssertPtrBreak(pDisk);
6150 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6151 Assert(!pDisk->fLocked);
6152
6153 rc = VDCloseAll(pDisk);
6154 int rc2 = VDFilterRemoveAll(pDisk);
6155 if (RT_SUCCESS(rc))
6156 rc = rc2;
6157
6158 RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
6159 RTMemCacheDestroy(pDisk->hMemCacheIoTask);
6160 RTMemFree(pDisk);
6161 } while (0);
6162 LogFlowFunc(("returns %Rrc\n", rc));
6163 return rc;
6164}
6165
6166/**
6167 * Try to get the backend name which can use this image.
6168 *
6169 * @returns VBox status code.
6170 * VINF_SUCCESS if a plugin was found.
6171 * ppszFormat contains the string which can be used as backend name.
6172 * VERR_NOT_SUPPORTED if no backend was found.
6173 * @param pVDIfsDisk Pointer to the per-disk VD interface list.
6174 * @param pVDIfsImage Pointer to the per-image VD interface list.
6175 * @param pszFilename Name of the image file for which the backend is queried.
6176 * @param ppszFormat Receives pointer of the UTF-8 string which contains the format name.
6177 * The returned pointer must be freed using RTStrFree().
6178 */
6179VBOXDDU_DECL(int) VDGetFormat(PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
6180 const char *pszFilename, char **ppszFormat, VDTYPE *penmType)
6181{
6182 int rc = VERR_NOT_SUPPORTED;
6183 VDINTERFACEIOINT VDIfIoInt;
6184 VDINTERFACEIO VDIfIoFallback;
6185 PVDINTERFACEIO pInterfaceIo;
6186
6187 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
6188 /* Check arguments. */
6189 AssertMsgReturn(VALID_PTR(pszFilename) && *pszFilename,
6190 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
6191 VERR_INVALID_PARAMETER);
6192 AssertMsgReturn(VALID_PTR(ppszFormat),
6193 ("ppszFormat=%#p\n", ppszFormat),
6194 VERR_INVALID_PARAMETER);
6195 AssertMsgReturn(VALID_PTR(penmType),
6196 ("penmType=%#p\n", penmType),
6197 VERR_INVALID_PARAMETER);
6198
6199 if (!g_apBackends)
6200 VDInit();
6201
6202 pInterfaceIo = VDIfIoGet(pVDIfsImage);
6203 if (!pInterfaceIo)
6204 {
6205 /*
6206 * Caller doesn't provide an I/O interface, create our own using the
6207 * native file API.
6208 */
6209 vdIfIoFallbackCallbacksSetup(&VDIfIoFallback);
6210 pInterfaceIo = &VDIfIoFallback;
6211 }
6212
6213 /* Set up the internal I/O interface. */
6214 AssertReturn(!VDIfIoIntGet(pVDIfsImage), VERR_INVALID_PARAMETER);
6215 VDIfIoInt.pfnOpen = vdIOIntOpenLimited;
6216 VDIfIoInt.pfnClose = vdIOIntCloseLimited;
6217 VDIfIoInt.pfnDelete = vdIOIntDeleteLimited;
6218 VDIfIoInt.pfnMove = vdIOIntMoveLimited;
6219 VDIfIoInt.pfnGetFreeSpace = vdIOIntGetFreeSpaceLimited;
6220 VDIfIoInt.pfnGetModificationTime = vdIOIntGetModificationTimeLimited;
6221 VDIfIoInt.pfnGetSize = vdIOIntGetSizeLimited;
6222 VDIfIoInt.pfnSetSize = vdIOIntSetSizeLimited;
6223 VDIfIoInt.pfnReadUser = vdIOIntReadUserLimited;
6224 VDIfIoInt.pfnWriteUser = vdIOIntWriteUserLimited;
6225 VDIfIoInt.pfnReadMeta = vdIOIntReadMetaLimited;
6226 VDIfIoInt.pfnWriteMeta = vdIOIntWriteMetaLimited;
6227 VDIfIoInt.pfnFlush = vdIOIntFlushLimited;
6228 rc = VDInterfaceAdd(&VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
6229 pInterfaceIo, sizeof(VDINTERFACEIOINT), &pVDIfsImage);
6230 AssertRC(rc);
6231
6232 /* Find the backend supporting this file format. */
6233 for (unsigned i = 0; i < g_cBackends; i++)
6234 {
6235 if (g_apBackends[i]->pfnCheckIfValid)
6236 {
6237 rc = g_apBackends[i]->pfnCheckIfValid(pszFilename, pVDIfsDisk,
6238 pVDIfsImage, penmType);
6239 if ( RT_SUCCESS(rc)
6240 /* The correct backend has been found, but there is a small
6241 * incompatibility so that the file cannot be used. Stop here
6242 * and signal success - the actual open will of course fail,
6243 * but that will create a really sensible error message. */
6244 || ( rc != VERR_VD_GEN_INVALID_HEADER
6245 && rc != VERR_VD_VDI_INVALID_HEADER
6246 && rc != VERR_VD_VMDK_INVALID_HEADER
6247 && rc != VERR_VD_ISCSI_INVALID_HEADER
6248 && rc != VERR_VD_VHD_INVALID_HEADER
6249 && rc != VERR_VD_RAW_INVALID_HEADER
6250 && rc != VERR_VD_RAW_SIZE_MODULO_512
6251 && rc != VERR_VD_RAW_SIZE_MODULO_2048
6252 && rc != VERR_VD_RAW_SIZE_OPTICAL_TOO_SMALL
6253 && rc != VERR_VD_RAW_SIZE_FLOPPY_TOO_BIG
6254 && rc != VERR_VD_PARALLELS_INVALID_HEADER
6255 && rc != VERR_VD_DMG_INVALID_HEADER))
6256 {
6257 /* Copy the name into the new string. */
6258 char *pszFormat = RTStrDup(g_apBackends[i]->pszBackendName);
6259 if (!pszFormat)
6260 {
6261 rc = VERR_NO_MEMORY;
6262 break;
6263 }
6264 *ppszFormat = pszFormat;
6265 /* Do not consider the typical file access errors as success,
6266 * which allows the caller to deal with such issues. */
6267 if ( rc != VERR_ACCESS_DENIED
6268 && rc != VERR_PATH_NOT_FOUND
6269 && rc != VERR_FILE_NOT_FOUND)
6270 rc = VINF_SUCCESS;
6271 break;
6272 }
6273 rc = VERR_NOT_SUPPORTED;
6274 }
6275 }
6276
6277 /* Try the cache backends. */
6278 if (rc == VERR_NOT_SUPPORTED)
6279 {
6280 for (unsigned i = 0; i < g_cCacheBackends; i++)
6281 {
6282 if (g_apCacheBackends[i]->pfnProbe)
6283 {
6284 rc = g_apCacheBackends[i]->pfnProbe(pszFilename, pVDIfsDisk,
6285 pVDIfsImage);
6286 if ( RT_SUCCESS(rc)
6287 || (rc != VERR_VD_GEN_INVALID_HEADER))
6288 {
6289 /* Copy the name into the new string. */
6290 char *pszFormat = RTStrDup(g_apBackends[i]->pszBackendName);
6291 if (!pszFormat)
6292 {
6293 rc = VERR_NO_MEMORY;
6294 break;
6295 }
6296 *ppszFormat = pszFormat;
6297 rc = VINF_SUCCESS;
6298 break;
6299 }
6300 rc = VERR_NOT_SUPPORTED;
6301 }
6302 }
6303 }
6304
6305 LogFlowFunc(("returns %Rrc *ppszFormat=\"%s\"\n", rc, *ppszFormat));
6306 return rc;
6307}
6308
6309/**
6310 * Opens an image file.
6311 *
6312 * The first opened image file in HDD container must have a base image type,
6313 * others (next opened images) must be a differencing or undo images.
6314 * Linkage is checked for differencing image to be in consistence with the previously opened image.
6315 * When another differencing image is opened and the last image was opened in read/write access
6316 * mode, then the last image is reopened in read-only with deny write sharing mode. This allows
6317 * other processes to use images in read-only mode too.
6318 *
6319 * Note that the image is opened in read-only mode if a read/write open is not possible.
6320 * Use VDIsReadOnly to check open mode.
6321 *
6322 * @returns VBox status code.
6323 * @param pDisk Pointer to HDD container.
6324 * @param pszBackend Name of the image file backend to use.
6325 * @param pszFilename Name of the image file to open.
6326 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
6327 * @param pVDIfsImage Pointer to the per-image VD interface list.
6328 */
6329VBOXDDU_DECL(int) VDOpen(PVBOXHDD pDisk, const char *pszBackend,
6330 const char *pszFilename, unsigned uOpenFlags,
6331 PVDINTERFACE pVDIfsImage)
6332{
6333 int rc = VINF_SUCCESS;
6334 int rc2;
6335 bool fLockWrite = false;
6336 PVDIMAGE pImage = NULL;
6337
6338 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x, pVDIfsImage=%#p\n",
6339 pDisk, pszBackend, pszFilename, uOpenFlags, pVDIfsImage));
6340
6341 do
6342 {
6343 /* sanity check */
6344 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6345 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6346
6347 /* Check arguments. */
6348 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
6349 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
6350 rc = VERR_INVALID_PARAMETER);
6351 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
6352 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
6353 rc = VERR_INVALID_PARAMETER);
6354 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
6355 ("uOpenFlags=%#x\n", uOpenFlags),
6356 rc = VERR_INVALID_PARAMETER);
6357 AssertMsgBreakStmt( !(uOpenFlags & VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)
6358 || (uOpenFlags & VD_OPEN_FLAGS_READONLY),
6359 ("uOpenFlags=%#x\n", uOpenFlags),
6360 rc = VERR_INVALID_PARAMETER);
6361
6362 /*
6363 * Destroy the current discard state first which might still have pending blocks
6364 * for the currently opened image which will be switched to readonly mode.
6365 */
6366 /* Lock disk for writing, as we modify pDisk information below. */
6367 rc2 = vdThreadStartWrite(pDisk);
6368 AssertRC(rc2);
6369 fLockWrite = true;
6370 rc = vdDiscardStateDestroy(pDisk);
6371 if (RT_FAILURE(rc))
6372 break;
6373 rc2 = vdThreadFinishWrite(pDisk);
6374 AssertRC(rc2);
6375 fLockWrite = false;
6376
6377 /* Set up image descriptor. */
6378 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
6379 if (!pImage)
6380 {
6381 rc = VERR_NO_MEMORY;
6382 break;
6383 }
6384 pImage->pszFilename = RTStrDup(pszFilename);
6385 if (!pImage->pszFilename)
6386 {
6387 rc = VERR_NO_MEMORY;
6388 break;
6389 }
6390
6391 pImage->VDIo.pDisk = pDisk;
6392 pImage->pVDIfsImage = pVDIfsImage;
6393
6394 rc = vdFindBackend(pszBackend, &pImage->Backend);
6395 if (RT_FAILURE(rc))
6396 break;
6397 if (!pImage->Backend)
6398 {
6399 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
6400 N_("VD: unknown backend name '%s'"), pszBackend);
6401 break;
6402 }
6403
6404 /*
6405 * Fail if the backend can't do async I/O but the
6406 * flag is set.
6407 */
6408 if ( !(pImage->Backend->uBackendCaps & VD_CAP_ASYNC)
6409 && (uOpenFlags & VD_OPEN_FLAGS_ASYNC_IO))
6410 {
6411 rc = vdError(pDisk, VERR_NOT_SUPPORTED, RT_SRC_POS,
6412 N_("VD: Backend '%s' does not support async I/O"), pszBackend);
6413 break;
6414 }
6415
6416 /*
6417 * Fail if the backend doesn't support the discard operation but the
6418 * flag is set.
6419 */
6420 if ( !(pImage->Backend->uBackendCaps & VD_CAP_DISCARD)
6421 && (uOpenFlags & VD_OPEN_FLAGS_DISCARD))
6422 {
6423 rc = vdError(pDisk, VERR_VD_DISCARD_NOT_SUPPORTED, RT_SRC_POS,
6424 N_("VD: Backend '%s' does not support discard"), pszBackend);
6425 break;
6426 }
6427
6428 /* Set up the I/O interface. */
6429 pImage->VDIo.pInterfaceIo = VDIfIoGet(pVDIfsImage);
6430 if (!pImage->VDIo.pInterfaceIo)
6431 {
6432 vdIfIoFallbackCallbacksSetup(&pImage->VDIo.VDIfIo);
6433 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIo.Core, "VD_IO", VDINTERFACETYPE_IO,
6434 pDisk, sizeof(VDINTERFACEIO), &pVDIfsImage);
6435 pImage->VDIo.pInterfaceIo = &pImage->VDIo.VDIfIo;
6436 }
6437
6438 /* Set up the internal I/O interface. */
6439 AssertBreakStmt(!VDIfIoIntGet(pVDIfsImage), rc = VERR_INVALID_PARAMETER);
6440 vdIfIoIntCallbacksSetup(&pImage->VDIo.VDIfIoInt);
6441 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
6442 &pImage->VDIo, sizeof(VDINTERFACEIOINT), &pImage->pVDIfsImage);
6443 AssertRC(rc);
6444
6445 pImage->uOpenFlags = uOpenFlags & (VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_DISCARD | VD_OPEN_FLAGS_IGNORE_FLUSH | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS);
6446 pImage->VDIo.fIgnoreFlush = (uOpenFlags & VD_OPEN_FLAGS_IGNORE_FLUSH) != 0;
6447 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
6448 uOpenFlags & ~(VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_IGNORE_FLUSH | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS),
6449 pDisk->pVDIfsDisk,
6450 pImage->pVDIfsImage,
6451 pDisk->enmType,
6452 &pImage->pBackendData);
6453 /*
6454 * If the image is corrupted and there is a repair method try to repair it
6455 * first if it was openend in read-write mode and open again afterwards.
6456 */
6457 if ( RT_UNLIKELY(rc == VERR_VD_IMAGE_CORRUPTED)
6458 && !(uOpenFlags & VD_OPEN_FLAGS_READONLY)
6459 && pImage->Backend->pfnRepair)
6460 {
6461 rc = pImage->Backend->pfnRepair(pszFilename, pDisk->pVDIfsDisk, pImage->pVDIfsImage, 0 /* fFlags */);
6462 if (RT_SUCCESS(rc))
6463 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
6464 uOpenFlags & ~(VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_IGNORE_FLUSH | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS),
6465 pDisk->pVDIfsDisk,
6466 pImage->pVDIfsImage,
6467 pDisk->enmType,
6468 &pImage->pBackendData);
6469 else
6470 {
6471 rc = vdError(pDisk, rc, RT_SRC_POS,
6472 N_("VD: error %Rrc repairing corrupted image file '%s'"), rc, pszFilename);
6473 break;
6474 }
6475 }
6476 else if (RT_UNLIKELY(rc == VERR_VD_IMAGE_CORRUPTED))
6477 {
6478 rc = vdError(pDisk, rc, RT_SRC_POS,
6479 N_("VD: Image file '%s' is corrupted and can't be opened"), pszFilename);
6480 break;
6481 }
6482
6483 /* If the open in read-write mode failed, retry in read-only mode. */
6484 if (RT_FAILURE(rc))
6485 {
6486 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
6487 && ( rc == VERR_ACCESS_DENIED
6488 || rc == VERR_PERMISSION_DENIED
6489 || rc == VERR_WRITE_PROTECT
6490 || rc == VERR_SHARING_VIOLATION
6491 || rc == VERR_FILE_LOCK_FAILED))
6492 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
6493 (uOpenFlags & ~(VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS))
6494 | VD_OPEN_FLAGS_READONLY,
6495 pDisk->pVDIfsDisk,
6496 pImage->pVDIfsImage,
6497 pDisk->enmType,
6498 &pImage->pBackendData);
6499 if (RT_FAILURE(rc))
6500 {
6501 rc = vdError(pDisk, rc, RT_SRC_POS,
6502 N_("VD: error %Rrc opening image file '%s'"), rc, pszFilename);
6503 break;
6504 }
6505 }
6506
6507 /* Lock disk for writing, as we modify pDisk information below. */
6508 rc2 = vdThreadStartWrite(pDisk);
6509 AssertRC(rc2);
6510 fLockWrite = true;
6511
6512 pImage->VDIo.pBackendData = pImage->pBackendData;
6513
6514 /* Check image type. As the image itself has only partial knowledge
6515 * whether it's a base image or not, this info is derived here. The
6516 * base image can be fixed or normal, all others must be normal or
6517 * diff images. Some image formats don't distinguish between normal
6518 * and diff images, so this must be corrected here. */
6519 unsigned uImageFlags;
6520 uImageFlags = pImage->Backend->pfnGetImageFlags(pImage->pBackendData);
6521 if (RT_FAILURE(rc))
6522 uImageFlags = VD_IMAGE_FLAGS_NONE;
6523 if ( RT_SUCCESS(rc)
6524 && !(uOpenFlags & VD_OPEN_FLAGS_INFO))
6525 {
6526 if ( pDisk->cImages == 0
6527 && (uImageFlags & VD_IMAGE_FLAGS_DIFF))
6528 {
6529 rc = VERR_VD_INVALID_TYPE;
6530 break;
6531 }
6532 else if (pDisk->cImages != 0)
6533 {
6534 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
6535 {
6536 rc = VERR_VD_INVALID_TYPE;
6537 break;
6538 }
6539 else
6540 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
6541 }
6542 }
6543
6544 /* Ensure we always get correct diff information, even if the backend
6545 * doesn't actually have a stored flag for this. It must not return
6546 * bogus information for the parent UUID if it is not a diff image. */
6547 RTUUID parentUuid;
6548 RTUuidClear(&parentUuid);
6549 rc2 = pImage->Backend->pfnGetParentUuid(pImage->pBackendData, &parentUuid);
6550 if (RT_SUCCESS(rc2) && !RTUuidIsNull(&parentUuid))
6551 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
6552
6553 pImage->uImageFlags = uImageFlags;
6554
6555 /* Force sane optimization settings. It's not worth avoiding writes
6556 * to fixed size images. The overhead would have almost no payback. */
6557 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
6558 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
6559
6560 /** @todo optionally check UUIDs */
6561
6562 /* Cache disk information. */
6563 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
6564
6565 /* Cache PCHS geometry. */
6566 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
6567 &pDisk->PCHSGeometry);
6568 if (RT_FAILURE(rc2))
6569 {
6570 pDisk->PCHSGeometry.cCylinders = 0;
6571 pDisk->PCHSGeometry.cHeads = 0;
6572 pDisk->PCHSGeometry.cSectors = 0;
6573 }
6574 else
6575 {
6576 /* Make sure the PCHS geometry is properly clipped. */
6577 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
6578 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
6579 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
6580 }
6581
6582 /* Cache LCHS geometry. */
6583 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
6584 &pDisk->LCHSGeometry);
6585 if (RT_FAILURE(rc2))
6586 {
6587 pDisk->LCHSGeometry.cCylinders = 0;
6588 pDisk->LCHSGeometry.cHeads = 0;
6589 pDisk->LCHSGeometry.cSectors = 0;
6590 }
6591 else
6592 {
6593 /* Make sure the LCHS geometry is properly clipped. */
6594 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
6595 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
6596 }
6597
6598 if (pDisk->cImages != 0)
6599 {
6600 /* Switch previous image to read-only mode. */
6601 unsigned uOpenFlagsPrevImg;
6602 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
6603 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
6604 {
6605 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
6606 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pBackendData, uOpenFlagsPrevImg);
6607 }
6608 }
6609
6610 if (RT_SUCCESS(rc))
6611 {
6612 /* Image successfully opened, make it the last image. */
6613 vdAddImageToList(pDisk, pImage);
6614 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
6615 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
6616 }
6617 else
6618 {
6619 /* Error detected, but image opened. Close image. */
6620 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, false);
6621 AssertRC(rc2);
6622 pImage->pBackendData = NULL;
6623 }
6624 } while (0);
6625
6626 if (RT_UNLIKELY(fLockWrite))
6627 {
6628 rc2 = vdThreadFinishWrite(pDisk);
6629 AssertRC(rc2);
6630 }
6631
6632 if (RT_FAILURE(rc))
6633 {
6634 if (pImage)
6635 {
6636 if (pImage->pszFilename)
6637 RTStrFree(pImage->pszFilename);
6638 RTMemFree(pImage);
6639 }
6640 }
6641
6642 LogFlowFunc(("returns %Rrc\n", rc));
6643 return rc;
6644}
6645
6646/**
6647 * Opens a cache image.
6648 *
6649 * @return VBox status code.
6650 * @param pDisk Pointer to the HDD container which should use the cache image.
6651 * @param pszBackend Name of the cache file backend to use (case insensitive).
6652 * @param pszFilename Name of the cache image to open.
6653 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
6654 * @param pVDIfsCache Pointer to the per-cache VD interface list.
6655 */
6656VBOXDDU_DECL(int) VDCacheOpen(PVBOXHDD pDisk, const char *pszBackend,
6657 const char *pszFilename, unsigned uOpenFlags,
6658 PVDINTERFACE pVDIfsCache)
6659{
6660 int rc = VINF_SUCCESS;
6661 int rc2;
6662 bool fLockWrite = false;
6663 PVDCACHE pCache = NULL;
6664
6665 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x, pVDIfsCache=%#p\n",
6666 pDisk, pszBackend, pszFilename, uOpenFlags, pVDIfsCache));
6667
6668 do
6669 {
6670 /* sanity check */
6671 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6672 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6673
6674 /* Check arguments. */
6675 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
6676 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
6677 rc = VERR_INVALID_PARAMETER);
6678 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
6679 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
6680 rc = VERR_INVALID_PARAMETER);
6681 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
6682 ("uOpenFlags=%#x\n", uOpenFlags),
6683 rc = VERR_INVALID_PARAMETER);
6684
6685 /* Set up image descriptor. */
6686 pCache = (PVDCACHE)RTMemAllocZ(sizeof(VDCACHE));
6687 if (!pCache)
6688 {
6689 rc = VERR_NO_MEMORY;
6690 break;
6691 }
6692 pCache->pszFilename = RTStrDup(pszFilename);
6693 if (!pCache->pszFilename)
6694 {
6695 rc = VERR_NO_MEMORY;
6696 break;
6697 }
6698
6699 pCache->VDIo.pDisk = pDisk;
6700 pCache->pVDIfsCache = pVDIfsCache;
6701
6702 rc = vdFindCacheBackend(pszBackend, &pCache->Backend);
6703 if (RT_FAILURE(rc))
6704 break;
6705 if (!pCache->Backend)
6706 {
6707 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
6708 N_("VD: unknown backend name '%s'"), pszBackend);
6709 break;
6710 }
6711
6712 /* Set up the I/O interface. */
6713 pCache->VDIo.pInterfaceIo = VDIfIoGet(pVDIfsCache);
6714 if (!pCache->VDIo.pInterfaceIo)
6715 {
6716 vdIfIoFallbackCallbacksSetup(&pCache->VDIo.VDIfIo);
6717 rc = VDInterfaceAdd(&pCache->VDIo.VDIfIo.Core, "VD_IO", VDINTERFACETYPE_IO,
6718 pDisk, sizeof(VDINTERFACEIO), &pVDIfsCache);
6719 pCache->VDIo.pInterfaceIo = &pCache->VDIo.VDIfIo;
6720 }
6721
6722 /* Set up the internal I/O interface. */
6723 AssertBreakStmt(!VDIfIoIntGet(pVDIfsCache), rc = VERR_INVALID_PARAMETER);
6724 vdIfIoIntCallbacksSetup(&pCache->VDIo.VDIfIoInt);
6725 rc = VDInterfaceAdd(&pCache->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
6726 &pCache->VDIo, sizeof(VDINTERFACEIOINT), &pCache->pVDIfsCache);
6727 AssertRC(rc);
6728
6729 pCache->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
6730 rc = pCache->Backend->pfnOpen(pCache->pszFilename,
6731 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
6732 pDisk->pVDIfsDisk,
6733 pCache->pVDIfsCache,
6734 &pCache->pBackendData);
6735 /* If the open in read-write mode failed, retry in read-only mode. */
6736 if (RT_FAILURE(rc))
6737 {
6738 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
6739 && ( rc == VERR_ACCESS_DENIED
6740 || rc == VERR_PERMISSION_DENIED
6741 || rc == VERR_WRITE_PROTECT
6742 || rc == VERR_SHARING_VIOLATION
6743 || rc == VERR_FILE_LOCK_FAILED))
6744 rc = pCache->Backend->pfnOpen(pCache->pszFilename,
6745 (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
6746 | VD_OPEN_FLAGS_READONLY,
6747 pDisk->pVDIfsDisk,
6748 pCache->pVDIfsCache,
6749 &pCache->pBackendData);
6750 if (RT_FAILURE(rc))
6751 {
6752 rc = vdError(pDisk, rc, RT_SRC_POS,
6753 N_("VD: error %Rrc opening image file '%s'"), rc, pszFilename);
6754 break;
6755 }
6756 }
6757
6758 /* Lock disk for writing, as we modify pDisk information below. */
6759 rc2 = vdThreadStartWrite(pDisk);
6760 AssertRC(rc2);
6761 fLockWrite = true;
6762
6763 /*
6764 * Check that the modification UUID of the cache and last image
6765 * match. If not the image was modified in-between without the cache.
6766 * The cache might contain stale data.
6767 */
6768 RTUUID UuidImage, UuidCache;
6769
6770 rc = pCache->Backend->pfnGetModificationUuid(pCache->pBackendData,
6771 &UuidCache);
6772 if (RT_SUCCESS(rc))
6773 {
6774 rc = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
6775 &UuidImage);
6776 if (RT_SUCCESS(rc))
6777 {
6778 if (RTUuidCompare(&UuidImage, &UuidCache))
6779 rc = VERR_VD_CACHE_NOT_UP_TO_DATE;
6780 }
6781 }
6782
6783 /*
6784 * We assume that the user knows what he is doing if one of the images
6785 * doesn't support the modification uuid.
6786 */
6787 if (rc == VERR_NOT_SUPPORTED)
6788 rc = VINF_SUCCESS;
6789
6790 if (RT_SUCCESS(rc))
6791 {
6792 /* Cache successfully opened, make it the current one. */
6793 if (!pDisk->pCache)
6794 pDisk->pCache = pCache;
6795 else
6796 rc = VERR_VD_CACHE_ALREADY_EXISTS;
6797 }
6798
6799 if (RT_FAILURE(rc))
6800 {
6801 /* Error detected, but image opened. Close image. */
6802 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, false);
6803 AssertRC(rc2);
6804 pCache->pBackendData = NULL;
6805 }
6806 } while (0);
6807
6808 if (RT_UNLIKELY(fLockWrite))
6809 {
6810 rc2 = vdThreadFinishWrite(pDisk);
6811 AssertRC(rc2);
6812 }
6813
6814 if (RT_FAILURE(rc))
6815 {
6816 if (pCache)
6817 {
6818 if (pCache->pszFilename)
6819 RTStrFree(pCache->pszFilename);
6820 RTMemFree(pCache);
6821 }
6822 }
6823
6824 LogFlowFunc(("returns %Rrc\n", rc));
6825 return rc;
6826}
6827
6828VBOXDDU_DECL(int) VDFilterAdd(PVBOXHDD pDisk, const char *pszFilter, uint32_t fFlags,
6829 PVDINTERFACE pVDIfsFilter)
6830{
6831 int rc = VINF_SUCCESS;
6832 int rc2;
6833 bool fLockWrite = false;
6834 PVDFILTER pFilter = NULL;
6835
6836 LogFlowFunc(("pDisk=%#p pszFilter=\"%s\" pVDIfsFilter=%#p\n",
6837 pDisk, pszFilter, pVDIfsFilter));
6838
6839 do
6840 {
6841 /* sanity check */
6842 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6843 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6844
6845 /* Check arguments. */
6846 AssertMsgBreakStmt(VALID_PTR(pszFilter) && *pszFilter,
6847 ("pszFilter=%#p \"%s\"\n", pszFilter, pszFilter),
6848 rc = VERR_INVALID_PARAMETER);
6849
6850 AssertMsgBreakStmt(!(fFlags & ~VD_FILTER_FLAGS_MASK),
6851 ("Invalid flags set (fFlags=%#x)\n", fFlags),
6852 rc = VERR_INVALID_PARAMETER);
6853
6854 /* Set up image descriptor. */
6855 pFilter = (PVDFILTER)RTMemAllocZ(sizeof(VDFILTER));
6856 if (!pFilter)
6857 {
6858 rc = VERR_NO_MEMORY;
6859 break;
6860 }
6861
6862 rc = vdFindFilterBackend(pszFilter, &pFilter->pBackend);
6863 if (RT_FAILURE(rc))
6864 break;
6865 if (!pFilter->pBackend)
6866 {
6867 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
6868 N_("VD: unknown filter backend name '%s'"), pszFilter);
6869 break;
6870 }
6871
6872 pFilter->VDIo.pDisk = pDisk;
6873 pFilter->pVDIfsFilter = pVDIfsFilter;
6874
6875 /* Set up the internal I/O interface. */
6876 AssertBreakStmt(!VDIfIoIntGet(pVDIfsFilter), rc = VERR_INVALID_PARAMETER);
6877 vdIfIoIntCallbacksSetup(&pFilter->VDIo.VDIfIoInt);
6878 rc = VDInterfaceAdd(&pFilter->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
6879 &pFilter->VDIo, sizeof(VDINTERFACEIOINT), &pFilter->pVDIfsFilter);
6880 AssertRC(rc);
6881
6882 rc = pFilter->pBackend->pfnCreate(pDisk->pVDIfsDisk, fFlags & VD_FILTER_FLAGS_INFO,
6883 pFilter->pVDIfsFilter, &pFilter->pvBackendData);
6884 if (RT_FAILURE(rc))
6885 break;
6886
6887 /* Lock disk for writing, as we modify pDisk information below. */
6888 rc2 = vdThreadStartWrite(pDisk);
6889 AssertRC(rc2);
6890 fLockWrite = true;
6891
6892 /* Add filter to chains. */
6893 if (fFlags & VD_FILTER_FLAGS_WRITE)
6894 {
6895 RTListAppend(&pDisk->ListFilterChainWrite, &pFilter->ListNodeChainWrite);
6896 vdFilterRetain(pFilter);
6897 }
6898
6899 if (fFlags & VD_FILTER_FLAGS_READ)
6900 {
6901 RTListAppend(&pDisk->ListFilterChainRead, &pFilter->ListNodeChainRead);
6902 vdFilterRetain(pFilter);
6903 }
6904 } while (0);
6905
6906 if (RT_UNLIKELY(fLockWrite))
6907 {
6908 rc2 = vdThreadFinishWrite(pDisk);
6909 AssertRC(rc2);
6910 }
6911
6912 if (RT_FAILURE(rc))
6913 {
6914 if (pFilter)
6915 RTMemFree(pFilter);
6916 }
6917
6918 LogFlowFunc(("returns %Rrc\n", rc));
6919 return rc;
6920}
6921
6922/**
6923 * Creates and opens a new base image file.
6924 *
6925 * @returns VBox status code.
6926 * @param pDisk Pointer to HDD container.
6927 * @param pszBackend Name of the image file backend to use.
6928 * @param pszFilename Name of the image file to create.
6929 * @param cbSize Image size in bytes.
6930 * @param uImageFlags Flags specifying special image features.
6931 * @param pszComment Pointer to image comment. NULL is ok.
6932 * @param pPCHSGeometry Pointer to physical disk geometry <= (16383,16,63). Not NULL.
6933 * @param pLCHSGeometry Pointer to logical disk geometry <= (x,255,63). Not NULL.
6934 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
6935 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
6936 * @param pVDIfsImage Pointer to the per-image VD interface list.
6937 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
6938 */
6939VBOXDDU_DECL(int) VDCreateBase(PVBOXHDD pDisk, const char *pszBackend,
6940 const char *pszFilename, uint64_t cbSize,
6941 unsigned uImageFlags, const char *pszComment,
6942 PCVDGEOMETRY pPCHSGeometry,
6943 PCVDGEOMETRY pLCHSGeometry,
6944 PCRTUUID pUuid, unsigned uOpenFlags,
6945 PVDINTERFACE pVDIfsImage,
6946 PVDINTERFACE pVDIfsOperation)
6947{
6948 int rc = VINF_SUCCESS;
6949 int rc2;
6950 bool fLockWrite = false, fLockRead = false;
6951 PVDIMAGE pImage = NULL;
6952 RTUUID uuid;
6953
6954 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",
6955 pDisk, pszBackend, pszFilename, cbSize, uImageFlags, pszComment,
6956 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
6957 pPCHSGeometry->cSectors, pLCHSGeometry->cCylinders,
6958 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors, pUuid,
6959 uOpenFlags, pVDIfsImage, pVDIfsOperation));
6960
6961 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
6962
6963 do
6964 {
6965 /* sanity check */
6966 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6967 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6968
6969 /* Check arguments. */
6970 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
6971 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
6972 rc = VERR_INVALID_PARAMETER);
6973 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
6974 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
6975 rc = VERR_INVALID_PARAMETER);
6976 AssertMsgBreakStmt(cbSize,
6977 ("cbSize=%llu\n", cbSize),
6978 rc = VERR_INVALID_PARAMETER);
6979 AssertMsgBreakStmt( ((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0)
6980 || ((uImageFlags & (VD_IMAGE_FLAGS_FIXED | VD_IMAGE_FLAGS_DIFF)) != VD_IMAGE_FLAGS_FIXED),
6981 ("uImageFlags=%#x\n", uImageFlags),
6982 rc = VERR_INVALID_PARAMETER);
6983 /* The PCHS geometry fields may be 0 to leave it for later. */
6984 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
6985 && pPCHSGeometry->cHeads <= 16
6986 && pPCHSGeometry->cSectors <= 63,
6987 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
6988 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
6989 pPCHSGeometry->cSectors),
6990 rc = VERR_INVALID_PARAMETER);
6991 /* The LCHS geometry fields may be 0 to leave it to later autodetection. */
6992 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
6993 && pLCHSGeometry->cHeads <= 255
6994 && pLCHSGeometry->cSectors <= 63,
6995 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
6996 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
6997 pLCHSGeometry->cSectors),
6998 rc = VERR_INVALID_PARAMETER);
6999 /* The UUID may be NULL. */
7000 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
7001 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
7002 rc = VERR_INVALID_PARAMETER);
7003 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
7004 ("uOpenFlags=%#x\n", uOpenFlags),
7005 rc = VERR_INVALID_PARAMETER);
7006
7007 /* Check state. Needs a temporary read lock. Holding the write lock
7008 * all the time would be blocking other activities for too long. */
7009 rc2 = vdThreadStartRead(pDisk);
7010 AssertRC(rc2);
7011 fLockRead = true;
7012 AssertMsgBreakStmt(pDisk->cImages == 0,
7013 ("Create base image cannot be done with other images open\n"),
7014 rc = VERR_VD_INVALID_STATE);
7015 rc2 = vdThreadFinishRead(pDisk);
7016 AssertRC(rc2);
7017 fLockRead = false;
7018
7019 /* Set up image descriptor. */
7020 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
7021 if (!pImage)
7022 {
7023 rc = VERR_NO_MEMORY;
7024 break;
7025 }
7026 pImage->pszFilename = RTStrDup(pszFilename);
7027 if (!pImage->pszFilename)
7028 {
7029 rc = VERR_NO_MEMORY;
7030 break;
7031 }
7032 pImage->VDIo.pDisk = pDisk;
7033 pImage->pVDIfsImage = pVDIfsImage;
7034
7035 /* Set up the I/O interface. */
7036 pImage->VDIo.pInterfaceIo = VDIfIoGet(pVDIfsImage);
7037 if (!pImage->VDIo.pInterfaceIo)
7038 {
7039 vdIfIoFallbackCallbacksSetup(&pImage->VDIo.VDIfIo);
7040 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIo.Core, "VD_IO", VDINTERFACETYPE_IO,
7041 pDisk, sizeof(VDINTERFACEIO), &pVDIfsImage);
7042 pImage->VDIo.pInterfaceIo = &pImage->VDIo.VDIfIo;
7043 }
7044
7045 /* Set up the internal I/O interface. */
7046 AssertBreakStmt(!VDIfIoIntGet(pVDIfsImage), rc = VERR_INVALID_PARAMETER);
7047 vdIfIoIntCallbacksSetup(&pImage->VDIo.VDIfIoInt);
7048 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
7049 &pImage->VDIo, sizeof(VDINTERFACEIOINT), &pImage->pVDIfsImage);
7050 AssertRC(rc);
7051
7052 rc = vdFindBackend(pszBackend, &pImage->Backend);
7053 if (RT_FAILURE(rc))
7054 break;
7055 if (!pImage->Backend)
7056 {
7057 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
7058 N_("VD: unknown backend name '%s'"), pszBackend);
7059 break;
7060 }
7061 if (!(pImage->Backend->uBackendCaps & ( VD_CAP_CREATE_FIXED
7062 | VD_CAP_CREATE_DYNAMIC)))
7063 {
7064 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
7065 N_("VD: backend '%s' cannot create base images"), pszBackend);
7066 break;
7067 }
7068
7069 /* Create UUID if the caller didn't specify one. */
7070 if (!pUuid)
7071 {
7072 rc = RTUuidCreate(&uuid);
7073 if (RT_FAILURE(rc))
7074 {
7075 rc = vdError(pDisk, rc, RT_SRC_POS,
7076 N_("VD: cannot generate UUID for image '%s'"),
7077 pszFilename);
7078 break;
7079 }
7080 pUuid = &uuid;
7081 }
7082
7083 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
7084 uImageFlags &= ~VD_IMAGE_FLAGS_DIFF;
7085 pImage->VDIo.fIgnoreFlush = (uOpenFlags & VD_OPEN_FLAGS_IGNORE_FLUSH) != 0;
7086 rc = pImage->Backend->pfnCreate(pImage->pszFilename, cbSize,
7087 uImageFlags, pszComment, pPCHSGeometry,
7088 pLCHSGeometry, pUuid,
7089 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
7090 0, 99,
7091 pDisk->pVDIfsDisk,
7092 pImage->pVDIfsImage,
7093 pVDIfsOperation,
7094 pDisk->enmType,
7095 &pImage->pBackendData);
7096
7097 if (RT_SUCCESS(rc))
7098 {
7099 pImage->VDIo.pBackendData = pImage->pBackendData;
7100 pImage->uImageFlags = uImageFlags;
7101
7102 /* Force sane optimization settings. It's not worth avoiding writes
7103 * to fixed size images. The overhead would have almost no payback. */
7104 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
7105 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
7106
7107 /* Lock disk for writing, as we modify pDisk information below. */
7108 rc2 = vdThreadStartWrite(pDisk);
7109 AssertRC(rc2);
7110 fLockWrite = true;
7111
7112 /** @todo optionally check UUIDs */
7113
7114 /* Re-check state, as the lock wasn't held and another image
7115 * creation call could have been done by another thread. */
7116 AssertMsgStmt(pDisk->cImages == 0,
7117 ("Create base image cannot be done with other images open\n"),
7118 rc = VERR_VD_INVALID_STATE);
7119 }
7120
7121 if (RT_SUCCESS(rc))
7122 {
7123 /* Cache disk information. */
7124 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
7125
7126 /* Cache PCHS geometry. */
7127 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
7128 &pDisk->PCHSGeometry);
7129 if (RT_FAILURE(rc2))
7130 {
7131 pDisk->PCHSGeometry.cCylinders = 0;
7132 pDisk->PCHSGeometry.cHeads = 0;
7133 pDisk->PCHSGeometry.cSectors = 0;
7134 }
7135 else
7136 {
7137 /* Make sure the CHS geometry is properly clipped. */
7138 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
7139 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
7140 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
7141 }
7142
7143 /* Cache LCHS geometry. */
7144 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
7145 &pDisk->LCHSGeometry);
7146 if (RT_FAILURE(rc2))
7147 {
7148 pDisk->LCHSGeometry.cCylinders = 0;
7149 pDisk->LCHSGeometry.cHeads = 0;
7150 pDisk->LCHSGeometry.cSectors = 0;
7151 }
7152 else
7153 {
7154 /* Make sure the CHS geometry is properly clipped. */
7155 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
7156 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
7157 }
7158
7159 /* Image successfully opened, make it the last image. */
7160 vdAddImageToList(pDisk, pImage);
7161 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
7162 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
7163 }
7164 else
7165 {
7166 /* Error detected, image may or may not be opened. Close and delete
7167 * image if it was opened. */
7168 if (pImage->pBackendData)
7169 {
7170 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, true);
7171 AssertRC(rc2);
7172 pImage->pBackendData = NULL;
7173 }
7174 }
7175 } while (0);
7176
7177 if (RT_UNLIKELY(fLockWrite))
7178 {
7179 rc2 = vdThreadFinishWrite(pDisk);
7180 AssertRC(rc2);
7181 }
7182 else if (RT_UNLIKELY(fLockRead))
7183 {
7184 rc2 = vdThreadFinishRead(pDisk);
7185 AssertRC(rc2);
7186 }
7187
7188 if (RT_FAILURE(rc))
7189 {
7190 if (pImage)
7191 {
7192 if (pImage->pszFilename)
7193 RTStrFree(pImage->pszFilename);
7194 RTMemFree(pImage);
7195 }
7196 }
7197
7198 if (RT_SUCCESS(rc) && pIfProgress && pIfProgress->pfnProgress)
7199 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
7200
7201 LogFlowFunc(("returns %Rrc\n", rc));
7202 return rc;
7203}
7204
7205/**
7206 * Creates and opens a new differencing image file in HDD container.
7207 * See comments for VDOpen function about differencing images.
7208 *
7209 * @returns VBox status code.
7210 * @param pDisk Pointer to HDD container.
7211 * @param pszBackend Name of the image file backend to use.
7212 * @param pszFilename Name of the differencing image file to create.
7213 * @param uImageFlags Flags specifying special image features.
7214 * @param pszComment Pointer to image comment. NULL is ok.
7215 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
7216 * @param pParentUuid New parent UUID of the image. If NULL, the UUID is queried automatically.
7217 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
7218 * @param pVDIfsImage Pointer to the per-image VD interface list.
7219 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
7220 */
7221VBOXDDU_DECL(int) VDCreateDiff(PVBOXHDD pDisk, const char *pszBackend,
7222 const char *pszFilename, unsigned uImageFlags,
7223 const char *pszComment, PCRTUUID pUuid,
7224 PCRTUUID pParentUuid, unsigned uOpenFlags,
7225 PVDINTERFACE pVDIfsImage,
7226 PVDINTERFACE pVDIfsOperation)
7227{
7228 int rc = VINF_SUCCESS;
7229 int rc2;
7230 bool fLockWrite = false, fLockRead = false;
7231 PVDIMAGE pImage = NULL;
7232 RTUUID uuid;
7233
7234 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
7235 pDisk, pszBackend, pszFilename, uImageFlags, pszComment, pUuid, uOpenFlags, pVDIfsImage, pVDIfsOperation));
7236
7237 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
7238
7239 do
7240 {
7241 /* sanity check */
7242 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7243 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7244
7245 /* Check arguments. */
7246 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
7247 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
7248 rc = VERR_INVALID_PARAMETER);
7249 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
7250 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
7251 rc = VERR_INVALID_PARAMETER);
7252 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
7253 ("uImageFlags=%#x\n", uImageFlags),
7254 rc = VERR_INVALID_PARAMETER);
7255 /* The UUID may be NULL. */
7256 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
7257 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
7258 rc = VERR_INVALID_PARAMETER);
7259 /* The parent UUID may be NULL. */
7260 AssertMsgBreakStmt(pParentUuid == NULL || VALID_PTR(pParentUuid),
7261 ("pParentUuid=%#p ParentUUID=%RTuuid\n", pParentUuid, pParentUuid),
7262 rc = VERR_INVALID_PARAMETER);
7263 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
7264 ("uOpenFlags=%#x\n", uOpenFlags),
7265 rc = VERR_INVALID_PARAMETER);
7266
7267 /* Check state. Needs a temporary read lock. Holding the write lock
7268 * all the time would be blocking other activities for too long. */
7269 rc2 = vdThreadStartRead(pDisk);
7270 AssertRC(rc2);
7271 fLockRead = true;
7272 AssertMsgBreakStmt(pDisk->cImages != 0,
7273 ("Create diff image cannot be done without other images open\n"),
7274 rc = VERR_VD_INVALID_STATE);
7275 rc2 = vdThreadFinishRead(pDisk);
7276 AssertRC(rc2);
7277 fLockRead = false;
7278
7279 /*
7280 * Destroy the current discard state first which might still have pending blocks
7281 * for the currently opened image which will be switched to readonly mode.
7282 */
7283 /* Lock disk for writing, as we modify pDisk information below. */
7284 rc2 = vdThreadStartWrite(pDisk);
7285 AssertRC(rc2);
7286 fLockWrite = true;
7287 rc = vdDiscardStateDestroy(pDisk);
7288 if (RT_FAILURE(rc))
7289 break;
7290 rc2 = vdThreadFinishWrite(pDisk);
7291 AssertRC(rc2);
7292 fLockWrite = false;
7293
7294 /* Set up image descriptor. */
7295 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
7296 if (!pImage)
7297 {
7298 rc = VERR_NO_MEMORY;
7299 break;
7300 }
7301 pImage->pszFilename = RTStrDup(pszFilename);
7302 if (!pImage->pszFilename)
7303 {
7304 rc = VERR_NO_MEMORY;
7305 break;
7306 }
7307
7308 rc = vdFindBackend(pszBackend, &pImage->Backend);
7309 if (RT_FAILURE(rc))
7310 break;
7311 if (!pImage->Backend)
7312 {
7313 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
7314 N_("VD: unknown backend name '%s'"), pszBackend);
7315 break;
7316 }
7317 if ( !(pImage->Backend->uBackendCaps & VD_CAP_DIFF)
7318 || !(pImage->Backend->uBackendCaps & ( VD_CAP_CREATE_FIXED
7319 | VD_CAP_CREATE_DYNAMIC)))
7320 {
7321 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
7322 N_("VD: backend '%s' cannot create diff images"), pszBackend);
7323 break;
7324 }
7325
7326 pImage->VDIo.pDisk = pDisk;
7327 pImage->pVDIfsImage = pVDIfsImage;
7328
7329 /* Set up the I/O interface. */
7330 pImage->VDIo.pInterfaceIo = VDIfIoGet(pVDIfsImage);
7331 if (!pImage->VDIo.pInterfaceIo)
7332 {
7333 vdIfIoFallbackCallbacksSetup(&pImage->VDIo.VDIfIo);
7334 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIo.Core, "VD_IO", VDINTERFACETYPE_IO,
7335 pDisk, sizeof(VDINTERFACEIO), &pVDIfsImage);
7336 pImage->VDIo.pInterfaceIo = &pImage->VDIo.VDIfIo;
7337 }
7338
7339 /* Set up the internal I/O interface. */
7340 AssertBreakStmt(!VDIfIoIntGet(pVDIfsImage), rc = VERR_INVALID_PARAMETER);
7341 vdIfIoIntCallbacksSetup(&pImage->VDIo.VDIfIoInt);
7342 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
7343 &pImage->VDIo, sizeof(VDINTERFACEIOINT), &pImage->pVDIfsImage);
7344 AssertRC(rc);
7345
7346 /* Create UUID if the caller didn't specify one. */
7347 if (!pUuid)
7348 {
7349 rc = RTUuidCreate(&uuid);
7350 if (RT_FAILURE(rc))
7351 {
7352 rc = vdError(pDisk, rc, RT_SRC_POS,
7353 N_("VD: cannot generate UUID for image '%s'"),
7354 pszFilename);
7355 break;
7356 }
7357 pUuid = &uuid;
7358 }
7359
7360 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
7361 pImage->VDIo.fIgnoreFlush = (uOpenFlags & VD_OPEN_FLAGS_IGNORE_FLUSH) != 0;
7362 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
7363 rc = pImage->Backend->pfnCreate(pImage->pszFilename, pDisk->cbSize,
7364 uImageFlags | VD_IMAGE_FLAGS_DIFF,
7365 pszComment, &pDisk->PCHSGeometry,
7366 &pDisk->LCHSGeometry, pUuid,
7367 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
7368 0, 99,
7369 pDisk->pVDIfsDisk,
7370 pImage->pVDIfsImage,
7371 pVDIfsOperation,
7372 pDisk->enmType,
7373 &pImage->pBackendData);
7374
7375 if (RT_SUCCESS(rc))
7376 {
7377 pImage->VDIo.pBackendData = pImage->pBackendData;
7378 pImage->uImageFlags = uImageFlags;
7379
7380 /* Lock disk for writing, as we modify pDisk information below. */
7381 rc2 = vdThreadStartWrite(pDisk);
7382 AssertRC(rc2);
7383 fLockWrite = true;
7384
7385 /* Switch previous image to read-only mode. */
7386 unsigned uOpenFlagsPrevImg;
7387 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
7388 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
7389 {
7390 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
7391 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pBackendData, uOpenFlagsPrevImg);
7392 }
7393
7394 /** @todo optionally check UUIDs */
7395
7396 /* Re-check state, as the lock wasn't held and another image
7397 * creation call could have been done by another thread. */
7398 AssertMsgStmt(pDisk->cImages != 0,
7399 ("Create diff image cannot be done without other images open\n"),
7400 rc = VERR_VD_INVALID_STATE);
7401 }
7402
7403 if (RT_SUCCESS(rc))
7404 {
7405 RTUUID Uuid;
7406 RTTIMESPEC ts;
7407
7408 if (pParentUuid && !RTUuidIsNull(pParentUuid))
7409 {
7410 Uuid = *pParentUuid;
7411 pImage->Backend->pfnSetParentUuid(pImage->pBackendData, &Uuid);
7412 }
7413 else
7414 {
7415 rc2 = pDisk->pLast->Backend->pfnGetUuid(pDisk->pLast->pBackendData,
7416 &Uuid);
7417 if (RT_SUCCESS(rc2))
7418 pImage->Backend->pfnSetParentUuid(pImage->pBackendData, &Uuid);
7419 }
7420 rc2 = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
7421 &Uuid);
7422 if (RT_SUCCESS(rc2))
7423 pImage->Backend->pfnSetParentModificationUuid(pImage->pBackendData,
7424 &Uuid);
7425 if (pDisk->pLast->Backend->pfnGetTimestamp)
7426 rc2 = pDisk->pLast->Backend->pfnGetTimestamp(pDisk->pLast->pBackendData,
7427 &ts);
7428 else
7429 rc2 = VERR_NOT_IMPLEMENTED;
7430 if (RT_SUCCESS(rc2) && pImage->Backend->pfnSetParentTimestamp)
7431 pImage->Backend->pfnSetParentTimestamp(pImage->pBackendData, &ts);
7432
7433 if (pImage->Backend->pfnSetParentFilename)
7434 rc2 = pImage->Backend->pfnSetParentFilename(pImage->pBackendData, pDisk->pLast->pszFilename);
7435 }
7436
7437 if (RT_SUCCESS(rc))
7438 {
7439 /* Image successfully opened, make it the last image. */
7440 vdAddImageToList(pDisk, pImage);
7441 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
7442 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
7443 }
7444 else
7445 {
7446 /* Error detected, but image opened. Close and delete image. */
7447 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, true);
7448 AssertRC(rc2);
7449 pImage->pBackendData = NULL;
7450 }
7451 } while (0);
7452
7453 if (RT_UNLIKELY(fLockWrite))
7454 {
7455 rc2 = vdThreadFinishWrite(pDisk);
7456 AssertRC(rc2);
7457 }
7458 else if (RT_UNLIKELY(fLockRead))
7459 {
7460 rc2 = vdThreadFinishRead(pDisk);
7461 AssertRC(rc2);
7462 }
7463
7464 if (RT_FAILURE(rc))
7465 {
7466 if (pImage)
7467 {
7468 if (pImage->pszFilename)
7469 RTStrFree(pImage->pszFilename);
7470 RTMemFree(pImage);
7471 }
7472 }
7473
7474 if (RT_SUCCESS(rc) && pIfProgress && pIfProgress->pfnProgress)
7475 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
7476
7477 LogFlowFunc(("returns %Rrc\n", rc));
7478 return rc;
7479}
7480
7481
7482/**
7483 * Creates and opens new cache image file in HDD container.
7484 *
7485 * @return VBox status code.
7486 * @param pDisk Name of the cache file backend to use (case insensitive).
7487 * @param pszFilename Name of the differencing cache file to create.
7488 * @param cbSize Maximum size of the cache.
7489 * @param uImageFlags Flags specifying special cache features.
7490 * @param pszComment Pointer to image comment. NULL is ok.
7491 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
7492 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
7493 * @param pVDIfsCache Pointer to the per-cache VD interface list.
7494 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
7495 */
7496VBOXDDU_DECL(int) VDCreateCache(PVBOXHDD pDisk, const char *pszBackend,
7497 const char *pszFilename, uint64_t cbSize,
7498 unsigned uImageFlags, const char *pszComment,
7499 PCRTUUID pUuid, unsigned uOpenFlags,
7500 PVDINTERFACE pVDIfsCache, PVDINTERFACE pVDIfsOperation)
7501{
7502 int rc = VINF_SUCCESS;
7503 int rc2;
7504 bool fLockWrite = false, fLockRead = false;
7505 PVDCACHE pCache = NULL;
7506 RTUUID uuid;
7507
7508 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
7509 pDisk, pszBackend, pszFilename, cbSize, uImageFlags, pszComment, pUuid, uOpenFlags, pVDIfsCache, pVDIfsOperation));
7510
7511 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
7512
7513 do
7514 {
7515 /* sanity check */
7516 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7517 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7518
7519 /* Check arguments. */
7520 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
7521 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
7522 rc = VERR_INVALID_PARAMETER);
7523 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
7524 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
7525 rc = VERR_INVALID_PARAMETER);
7526 AssertMsgBreakStmt(cbSize,
7527 ("cbSize=%llu\n", cbSize),
7528 rc = VERR_INVALID_PARAMETER);
7529 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
7530 ("uImageFlags=%#x\n", uImageFlags),
7531 rc = VERR_INVALID_PARAMETER);
7532 /* The UUID may be NULL. */
7533 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
7534 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
7535 rc = VERR_INVALID_PARAMETER);
7536 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
7537 ("uOpenFlags=%#x\n", uOpenFlags),
7538 rc = VERR_INVALID_PARAMETER);
7539
7540 /* Check state. Needs a temporary read lock. Holding the write lock
7541 * all the time would be blocking other activities for too long. */
7542 rc2 = vdThreadStartRead(pDisk);
7543 AssertRC(rc2);
7544 fLockRead = true;
7545 AssertMsgBreakStmt(!pDisk->pCache,
7546 ("Create cache image cannot be done with a cache already attached\n"),
7547 rc = VERR_VD_CACHE_ALREADY_EXISTS);
7548 rc2 = vdThreadFinishRead(pDisk);
7549 AssertRC(rc2);
7550 fLockRead = false;
7551
7552 /* Set up image descriptor. */
7553 pCache = (PVDCACHE)RTMemAllocZ(sizeof(VDCACHE));
7554 if (!pCache)
7555 {
7556 rc = VERR_NO_MEMORY;
7557 break;
7558 }
7559 pCache->pszFilename = RTStrDup(pszFilename);
7560 if (!pCache->pszFilename)
7561 {
7562 rc = VERR_NO_MEMORY;
7563 break;
7564 }
7565
7566 rc = vdFindCacheBackend(pszBackend, &pCache->Backend);
7567 if (RT_FAILURE(rc))
7568 break;
7569 if (!pCache->Backend)
7570 {
7571 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
7572 N_("VD: unknown backend name '%s'"), pszBackend);
7573 break;
7574 }
7575
7576 pCache->VDIo.pDisk = pDisk;
7577 pCache->pVDIfsCache = pVDIfsCache;
7578
7579 /* Set up the I/O interface. */
7580 pCache->VDIo.pInterfaceIo = VDIfIoGet(pVDIfsCache);
7581 if (!pCache->VDIo.pInterfaceIo)
7582 {
7583 vdIfIoFallbackCallbacksSetup(&pCache->VDIo.VDIfIo);
7584 rc = VDInterfaceAdd(&pCache->VDIo.VDIfIo.Core, "VD_IO", VDINTERFACETYPE_IO,
7585 pDisk, sizeof(VDINTERFACEIO), &pVDIfsCache);
7586 pCache->VDIo.pInterfaceIo = &pCache->VDIo.VDIfIo;
7587 }
7588
7589 /* Set up the internal I/O interface. */
7590 AssertBreakStmt(!VDIfIoIntGet(pVDIfsCache), rc = VERR_INVALID_PARAMETER);
7591 vdIfIoIntCallbacksSetup(&pCache->VDIo.VDIfIoInt);
7592 rc = VDInterfaceAdd(&pCache->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
7593 &pCache->VDIo, sizeof(VDINTERFACEIOINT), &pCache->pVDIfsCache);
7594 AssertRC(rc);
7595
7596 /* Create UUID if the caller didn't specify one. */
7597 if (!pUuid)
7598 {
7599 rc = RTUuidCreate(&uuid);
7600 if (RT_FAILURE(rc))
7601 {
7602 rc = vdError(pDisk, rc, RT_SRC_POS,
7603 N_("VD: cannot generate UUID for image '%s'"),
7604 pszFilename);
7605 break;
7606 }
7607 pUuid = &uuid;
7608 }
7609
7610 pCache->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
7611 pCache->VDIo.fIgnoreFlush = (uOpenFlags & VD_OPEN_FLAGS_IGNORE_FLUSH) != 0;
7612 rc = pCache->Backend->pfnCreate(pCache->pszFilename, cbSize,
7613 uImageFlags,
7614 pszComment, pUuid,
7615 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
7616 0, 99,
7617 pDisk->pVDIfsDisk,
7618 pCache->pVDIfsCache,
7619 pVDIfsOperation,
7620 &pCache->pBackendData);
7621
7622 if (RT_SUCCESS(rc))
7623 {
7624 /* Lock disk for writing, as we modify pDisk information below. */
7625 rc2 = vdThreadStartWrite(pDisk);
7626 AssertRC(rc2);
7627 fLockWrite = true;
7628
7629 pCache->VDIo.pBackendData = pCache->pBackendData;
7630
7631 /* Re-check state, as the lock wasn't held and another image
7632 * creation call could have been done by another thread. */
7633 AssertMsgStmt(!pDisk->pCache,
7634 ("Create cache image cannot be done with another cache open\n"),
7635 rc = VERR_VD_CACHE_ALREADY_EXISTS);
7636 }
7637
7638 if ( RT_SUCCESS(rc)
7639 && pDisk->pLast)
7640 {
7641 RTUUID UuidModification;
7642
7643 /* Set same modification Uuid as the last image. */
7644 rc = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
7645 &UuidModification);
7646 if (RT_SUCCESS(rc))
7647 {
7648 rc = pCache->Backend->pfnSetModificationUuid(pCache->pBackendData,
7649 &UuidModification);
7650 }
7651
7652 if (rc == VERR_NOT_SUPPORTED)
7653 rc = VINF_SUCCESS;
7654 }
7655
7656 if (RT_SUCCESS(rc))
7657 {
7658 /* Cache successfully created. */
7659 pDisk->pCache = pCache;
7660 }
7661 else
7662 {
7663 /* Error detected, but image opened. Close and delete image. */
7664 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, true);
7665 AssertRC(rc2);
7666 pCache->pBackendData = NULL;
7667 }
7668 } while (0);
7669
7670 if (RT_UNLIKELY(fLockWrite))
7671 {
7672 rc2 = vdThreadFinishWrite(pDisk);
7673 AssertRC(rc2);
7674 }
7675 else if (RT_UNLIKELY(fLockRead))
7676 {
7677 rc2 = vdThreadFinishRead(pDisk);
7678 AssertRC(rc2);
7679 }
7680
7681 if (RT_FAILURE(rc))
7682 {
7683 if (pCache)
7684 {
7685 if (pCache->pszFilename)
7686 RTStrFree(pCache->pszFilename);
7687 RTMemFree(pCache);
7688 }
7689 }
7690
7691 if (RT_SUCCESS(rc) && pIfProgress && pIfProgress->pfnProgress)
7692 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
7693
7694 LogFlowFunc(("returns %Rrc\n", rc));
7695 return rc;
7696}
7697
7698/**
7699 * Merges two images (not necessarily with direct parent/child relationship).
7700 * As a side effect the source image and potentially the other images which
7701 * are also merged to the destination are deleted from both the disk and the
7702 * images in the HDD container.
7703 *
7704 * @returns VBox status code.
7705 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7706 * @param pDisk Pointer to HDD container.
7707 * @param nImageFrom Name of the image file to merge from.
7708 * @param nImageTo Name of the image file to merge to.
7709 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
7710 */
7711VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
7712 unsigned nImageTo, PVDINTERFACE pVDIfsOperation)
7713{
7714 int rc = VINF_SUCCESS;
7715 int rc2;
7716 bool fLockWrite = false, fLockRead = false;
7717 void *pvBuf = NULL;
7718
7719 LogFlowFunc(("pDisk=%#p nImageFrom=%u nImageTo=%u pVDIfsOperation=%#p\n",
7720 pDisk, nImageFrom, nImageTo, pVDIfsOperation));
7721
7722 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
7723
7724 do
7725 {
7726 /* sanity check */
7727 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7728 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7729
7730 /* For simplicity reasons lock for writing as the image reopen below
7731 * might need it. After all the reopen is usually needed. */
7732 rc2 = vdThreadStartWrite(pDisk);
7733 AssertRC(rc2);
7734 fLockWrite = true;
7735 PVDIMAGE pImageFrom = vdGetImageByNumber(pDisk, nImageFrom);
7736 PVDIMAGE pImageTo = vdGetImageByNumber(pDisk, nImageTo);
7737 if (!pImageFrom || !pImageTo)
7738 {
7739 rc = VERR_VD_IMAGE_NOT_FOUND;
7740 break;
7741 }
7742 AssertBreakStmt(pImageFrom != pImageTo, rc = VERR_INVALID_PARAMETER);
7743
7744 /* Make sure destination image is writable. */
7745 unsigned uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pBackendData);
7746 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
7747 {
7748 /*
7749 * Clear skip consistency checks because the image is made writable now and
7750 * skipping consistency checks is only possible for readonly images.
7751 */
7752 uOpenFlags &= ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS);
7753 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
7754 uOpenFlags);
7755 if (RT_FAILURE(rc))
7756 break;
7757 }
7758
7759 /* Get size of destination image. */
7760 uint64_t cbSize = pImageTo->Backend->pfnGetSize(pImageTo->pBackendData);
7761 rc2 = vdThreadFinishWrite(pDisk);
7762 AssertRC(rc2);
7763 fLockWrite = false;
7764
7765 /* Allocate tmp buffer. */
7766 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
7767 if (!pvBuf)
7768 {
7769 rc = VERR_NO_MEMORY;
7770 break;
7771 }
7772
7773 /* Merging is done directly on the images itself. This potentially
7774 * causes trouble if the disk is full in the middle of operation. */
7775 if (nImageFrom < nImageTo)
7776 {
7777 /* Merge parent state into child. This means writing all not
7778 * allocated blocks in the destination image which are allocated in
7779 * the images to be merged. */
7780 uint64_t uOffset = 0;
7781 uint64_t cbRemaining = cbSize;
7782 do
7783 {
7784 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
7785 RTSGSEG SegmentBuf;
7786 RTSGBUF SgBuf;
7787 VDIOCTX IoCtx;
7788
7789 SegmentBuf.pvSeg = pvBuf;
7790 SegmentBuf.cbSeg = VD_MERGE_BUFFER_SIZE;
7791 RTSgBufInit(&SgBuf, &SegmentBuf, 1);
7792 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_READ, 0, 0, NULL,
7793 &SgBuf, NULL, NULL, VDIOCTX_FLAGS_SYNC);
7794
7795 /* Need to hold the write lock during a read-write operation. */
7796 rc2 = vdThreadStartWrite(pDisk);
7797 AssertRC(rc2);
7798 fLockWrite = true;
7799
7800 rc = pImageTo->Backend->pfnRead(pImageTo->pBackendData,
7801 uOffset, cbThisRead,
7802 &IoCtx, &cbThisRead);
7803 if (rc == VERR_VD_BLOCK_FREE)
7804 {
7805 /* Search for image with allocated block. Do not attempt to
7806 * read more than the previous reads marked as valid.
7807 * Otherwise this would return stale data when different
7808 * block sizes are used for the images. */
7809 for (PVDIMAGE pCurrImage = pImageTo->pPrev;
7810 pCurrImage != NULL && pCurrImage != pImageFrom->pPrev && rc == VERR_VD_BLOCK_FREE;
7811 pCurrImage = pCurrImage->pPrev)
7812 {
7813 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
7814 uOffset, cbThisRead,
7815 &IoCtx, &cbThisRead);
7816 }
7817
7818 if (rc != VERR_VD_BLOCK_FREE)
7819 {
7820 if (RT_FAILURE(rc))
7821 break;
7822 /* Updating the cache is required because this might be a live merge. */
7823 rc = vdWriteHelperEx(pDisk, pImageTo, pImageFrom->pPrev,
7824 uOffset, pvBuf, cbThisRead,
7825 VDIOCTX_FLAGS_READ_UPDATE_CACHE, 0);
7826 if (RT_FAILURE(rc))
7827 break;
7828 }
7829 else
7830 rc = VINF_SUCCESS;
7831 }
7832 else if (RT_FAILURE(rc))
7833 break;
7834
7835 rc2 = vdThreadFinishWrite(pDisk);
7836 AssertRC(rc2);
7837 fLockWrite = false;
7838
7839 uOffset += cbThisRead;
7840 cbRemaining -= cbThisRead;
7841
7842 if (pIfProgress && pIfProgress->pfnProgress)
7843 {
7844 /** @todo r=klaus: this can update the progress to the same
7845 * percentage over and over again if the image format makes
7846 * relatively small increments. */
7847 rc = pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
7848 uOffset * 99 / cbSize);
7849 if (RT_FAILURE(rc))
7850 break;
7851 }
7852 } while (uOffset < cbSize);
7853 }
7854 else
7855 {
7856 /*
7857 * We may need to update the parent uuid of the child coming after
7858 * the last image to be merged. We have to reopen it read/write.
7859 *
7860 * This is done before we do the actual merge to prevent an
7861 * inconsistent chain if the mode change fails for some reason.
7862 */
7863 if (pImageFrom->pNext)
7864 {
7865 PVDIMAGE pImageChild = pImageFrom->pNext;
7866
7867 /* Take the write lock. */
7868 rc2 = vdThreadStartWrite(pDisk);
7869 AssertRC(rc2);
7870 fLockWrite = true;
7871
7872 /* We need to open the image in read/write mode. */
7873 uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pBackendData);
7874
7875 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
7876 {
7877 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
7878 rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pBackendData,
7879 uOpenFlags);
7880 if (RT_FAILURE(rc))
7881 break;
7882 }
7883
7884 rc2 = vdThreadFinishWrite(pDisk);
7885 AssertRC(rc2);
7886 fLockWrite = false;
7887 }
7888
7889 /* If the merge is from the last image we have to relay all writes
7890 * to the merge destination as well, so that concurrent writes
7891 * (in case of a live merge) are handled correctly. */
7892 if (!pImageFrom->pNext)
7893 {
7894 /* Take the write lock. */
7895 rc2 = vdThreadStartWrite(pDisk);
7896 AssertRC(rc2);
7897 fLockWrite = true;
7898
7899 pDisk->pImageRelay = pImageTo;
7900
7901 rc2 = vdThreadFinishWrite(pDisk);
7902 AssertRC(rc2);
7903 fLockWrite = false;
7904 }
7905
7906 /* Merge child state into parent. This means writing all blocks
7907 * which are allocated in the image up to the source image to the
7908 * destination image. */
7909 uint64_t uOffset = 0;
7910 uint64_t cbRemaining = cbSize;
7911 do
7912 {
7913 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
7914 RTSGSEG SegmentBuf;
7915 RTSGBUF SgBuf;
7916 VDIOCTX IoCtx;
7917
7918 rc = VERR_VD_BLOCK_FREE;
7919
7920 SegmentBuf.pvSeg = pvBuf;
7921 SegmentBuf.cbSeg = VD_MERGE_BUFFER_SIZE;
7922 RTSgBufInit(&SgBuf, &SegmentBuf, 1);
7923 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_READ, 0, 0, NULL,
7924 &SgBuf, NULL, NULL, VDIOCTX_FLAGS_SYNC);
7925
7926 /* Need to hold the write lock during a read-write operation. */
7927 rc2 = vdThreadStartWrite(pDisk);
7928 AssertRC(rc2);
7929 fLockWrite = true;
7930
7931 /* Search for image with allocated block. Do not attempt to
7932 * read more than the previous reads marked as valid. Otherwise
7933 * this would return stale data when different block sizes are
7934 * used for the images. */
7935 for (PVDIMAGE pCurrImage = pImageFrom;
7936 pCurrImage != NULL && pCurrImage != pImageTo && rc == VERR_VD_BLOCK_FREE;
7937 pCurrImage = pCurrImage->pPrev)
7938 {
7939 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
7940 uOffset, cbThisRead,
7941 &IoCtx, &cbThisRead);
7942 }
7943
7944 if (rc != VERR_VD_BLOCK_FREE)
7945 {
7946 if (RT_FAILURE(rc))
7947 break;
7948 rc = vdWriteHelper(pDisk, pImageTo, uOffset, pvBuf,
7949 cbThisRead, VDIOCTX_FLAGS_READ_UPDATE_CACHE);
7950 if (RT_FAILURE(rc))
7951 break;
7952 }
7953 else
7954 rc = VINF_SUCCESS;
7955
7956 rc2 = vdThreadFinishWrite(pDisk);
7957 AssertRC(rc2);
7958 fLockWrite = false;
7959
7960 uOffset += cbThisRead;
7961 cbRemaining -= cbThisRead;
7962
7963 if (pIfProgress && pIfProgress->pfnProgress)
7964 {
7965 /** @todo r=klaus: this can update the progress to the same
7966 * percentage over and over again if the image format makes
7967 * relatively small increments. */
7968 rc = pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
7969 uOffset * 99 / cbSize);
7970 if (RT_FAILURE(rc))
7971 break;
7972 }
7973 } while (uOffset < cbSize);
7974
7975 /* In case we set up a "write proxy" image above we must clear
7976 * this again now to prevent stray writes. Failure or not. */
7977 if (!pImageFrom->pNext)
7978 {
7979 /* Take the write lock. */
7980 rc2 = vdThreadStartWrite(pDisk);
7981 AssertRC(rc2);
7982 fLockWrite = true;
7983
7984 pDisk->pImageRelay = NULL;
7985
7986 rc2 = vdThreadFinishWrite(pDisk);
7987 AssertRC(rc2);
7988 fLockWrite = false;
7989 }
7990 }
7991
7992 /*
7993 * Leave in case of an error to avoid corrupted data in the image chain
7994 * (includes cancelling the operation by the user).
7995 */
7996 if (RT_FAILURE(rc))
7997 break;
7998
7999 /* Need to hold the write lock while finishing the merge. */
8000 rc2 = vdThreadStartWrite(pDisk);
8001 AssertRC(rc2);
8002 fLockWrite = true;
8003
8004 /* Update parent UUID so that image chain is consistent.
8005 * The two attempts work around the problem that some backends
8006 * (e.g. iSCSI) do not support UUIDs, so we exploit the fact that
8007 * so far there can only be one such image in the chain. */
8008 /** @todo needs a better long-term solution, passing the UUID
8009 * knowledge from the caller or some such */
8010 RTUUID Uuid;
8011 PVDIMAGE pImageChild = NULL;
8012 if (nImageFrom < nImageTo)
8013 {
8014 if (pImageFrom->pPrev)
8015 {
8016 /* plan A: ask the parent itself for its UUID */
8017 rc = pImageFrom->pPrev->Backend->pfnGetUuid(pImageFrom->pPrev->pBackendData,
8018 &Uuid);
8019 if (RT_FAILURE(rc))
8020 {
8021 /* plan B: ask the child of the parent for parent UUID */
8022 rc = pImageFrom->Backend->pfnGetParentUuid(pImageFrom->pBackendData,
8023 &Uuid);
8024 }
8025 AssertRC(rc);
8026 }
8027 else
8028 RTUuidClear(&Uuid);
8029 rc = pImageTo->Backend->pfnSetParentUuid(pImageTo->pBackendData,
8030 &Uuid);
8031 AssertRC(rc);
8032 }
8033 else
8034 {
8035 /* Update the parent uuid of the child of the last merged image. */
8036 if (pImageFrom->pNext)
8037 {
8038 /* plan A: ask the parent itself for its UUID */
8039 rc = pImageTo->Backend->pfnGetUuid(pImageTo->pBackendData,
8040 &Uuid);
8041 if (RT_FAILURE(rc))
8042 {
8043 /* plan B: ask the child of the parent for parent UUID */
8044 rc = pImageTo->pNext->Backend->pfnGetParentUuid(pImageTo->pNext->pBackendData,
8045 &Uuid);
8046 }
8047 AssertRC(rc);
8048
8049 rc = pImageFrom->Backend->pfnSetParentUuid(pImageFrom->pNext->pBackendData,
8050 &Uuid);
8051 AssertRC(rc);
8052
8053 pImageChild = pImageFrom->pNext;
8054 }
8055 }
8056
8057 /* Delete the no longer needed images. */
8058 PVDIMAGE pImg = pImageFrom, pTmp;
8059 while (pImg != pImageTo)
8060 {
8061 if (nImageFrom < nImageTo)
8062 pTmp = pImg->pNext;
8063 else
8064 pTmp = pImg->pPrev;
8065 vdRemoveImageFromList(pDisk, pImg);
8066 pImg->Backend->pfnClose(pImg->pBackendData, true);
8067 RTMemFree(pImg->pszFilename);
8068 RTMemFree(pImg);
8069 pImg = pTmp;
8070 }
8071
8072 /* Make sure destination image is back to read only if necessary. */
8073 if (pImageTo != pDisk->pLast)
8074 {
8075 uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pBackendData);
8076 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
8077 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
8078 uOpenFlags);
8079 if (RT_FAILURE(rc))
8080 break;
8081 }
8082
8083 /*
8084 * Make sure the child is readonly
8085 * for the child -> parent merge direction
8086 * if necessary.
8087 */
8088 if ( nImageFrom > nImageTo
8089 && pImageChild
8090 && pImageChild != pDisk->pLast)
8091 {
8092 uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pBackendData);
8093 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
8094 rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pBackendData,
8095 uOpenFlags);
8096 if (RT_FAILURE(rc))
8097 break;
8098 }
8099 } while (0);
8100
8101 if (RT_UNLIKELY(fLockWrite))
8102 {
8103 rc2 = vdThreadFinishWrite(pDisk);
8104 AssertRC(rc2);
8105 }
8106 else if (RT_UNLIKELY(fLockRead))
8107 {
8108 rc2 = vdThreadFinishRead(pDisk);
8109 AssertRC(rc2);
8110 }
8111
8112 if (pvBuf)
8113 RTMemTmpFree(pvBuf);
8114
8115 if (RT_SUCCESS(rc) && pIfProgress && pIfProgress->pfnProgress)
8116 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
8117
8118 LogFlowFunc(("returns %Rrc\n", rc));
8119 return rc;
8120}
8121
8122/**
8123 * Copies an image from one HDD container to another - extended version.
8124 * The copy is opened in the target HDD container.
8125 * It is possible to convert between different image formats, because the
8126 * backend for the destination may be different from the source.
8127 * If both the source and destination reference the same HDD container,
8128 * then the image is moved (by copying/deleting or renaming) to the new location.
8129 * The source container is unchanged if the move operation fails, otherwise
8130 * the image at the new location is opened in the same way as the old one was.
8131 *
8132 * @note The read/write accesses across disks are not synchronized, just the
8133 * accesses to each disk. Once there is a use case which requires a defined
8134 * read/write behavior in this situation this needs to be extended.
8135 *
8136 * @return VBox status code.
8137 * @return VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
8138 * @param pDiskFrom Pointer to source HDD container.
8139 * @param nImage Image number, counts from 0. 0 is always base image of container.
8140 * @param pDiskTo Pointer to destination HDD container.
8141 * @param pszBackend Name of the image file backend to use (may be NULL to use the same as the source, case insensitive).
8142 * @param pszFilename New name of the image (may be NULL to specify that the
8143 * copy destination is the destination container, or
8144 * if pDiskFrom == pDiskTo, i.e. when moving).
8145 * @param fMoveByRename If true, attempt to perform a move by renaming (if successful the new size is ignored).
8146 * @param cbSize New image size (0 means leave unchanged).
8147 * @param nImageSameFrom todo
8148 * @param nImageSameTo todo
8149 * @param uImageFlags Flags specifying special destination image features.
8150 * @param pDstUuid New UUID of the destination image. If NULL, a new UUID is created.
8151 * This parameter is used if and only if a true copy is created.
8152 * In all rename/move cases or copy to existing image cases the modification UUIDs are copied over.
8153 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
8154 * Only used if the destination image is created.
8155 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
8156 * @param pDstVDIfsImage Pointer to the per-image VD interface list, for the
8157 * destination image.
8158 * @param pDstVDIfsOperation Pointer to the per-operation VD interface list,
8159 * for the destination operation.
8160 */
8161VBOXDDU_DECL(int) VDCopyEx(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
8162 const char *pszBackend, const char *pszFilename,
8163 bool fMoveByRename, uint64_t cbSize,
8164 unsigned nImageFromSame, unsigned nImageToSame,
8165 unsigned uImageFlags, PCRTUUID pDstUuid,
8166 unsigned uOpenFlags, PVDINTERFACE pVDIfsOperation,
8167 PVDINTERFACE pDstVDIfsImage,
8168 PVDINTERFACE pDstVDIfsOperation)
8169{
8170 int rc = VINF_SUCCESS;
8171 int rc2;
8172 bool fLockReadFrom = false, fLockWriteFrom = false, fLockWriteTo = false;
8173 PVDIMAGE pImageTo = NULL;
8174
8175 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",
8176 pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename, cbSize, nImageFromSame, nImageToSame, uImageFlags, pDstUuid, uOpenFlags, pVDIfsOperation, pDstVDIfsImage, pDstVDIfsOperation));
8177
8178 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
8179 PVDINTERFACEPROGRESS pDstIfProgress = VDIfProgressGet(pDstVDIfsOperation);
8180
8181 do {
8182 /* Check arguments. */
8183 AssertMsgBreakStmt(VALID_PTR(pDiskFrom), ("pDiskFrom=%#p\n", pDiskFrom),
8184 rc = VERR_INVALID_PARAMETER);
8185 AssertMsg(pDiskFrom->u32Signature == VBOXHDDDISK_SIGNATURE,
8186 ("u32Signature=%08x\n", pDiskFrom->u32Signature));
8187
8188 rc2 = vdThreadStartRead(pDiskFrom);
8189 AssertRC(rc2);
8190 fLockReadFrom = true;
8191 PVDIMAGE pImageFrom = vdGetImageByNumber(pDiskFrom, nImage);
8192 AssertPtrBreakStmt(pImageFrom, rc = VERR_VD_IMAGE_NOT_FOUND);
8193 AssertMsgBreakStmt(VALID_PTR(pDiskTo), ("pDiskTo=%#p\n", pDiskTo),
8194 rc = VERR_INVALID_PARAMETER);
8195 AssertMsg(pDiskTo->u32Signature == VBOXHDDDISK_SIGNATURE,
8196 ("u32Signature=%08x\n", pDiskTo->u32Signature));
8197 AssertMsgBreakStmt( (nImageFromSame < nImage || nImageFromSame == VD_IMAGE_CONTENT_UNKNOWN)
8198 && (nImageToSame < pDiskTo->cImages || nImageToSame == VD_IMAGE_CONTENT_UNKNOWN)
8199 && ( (nImageFromSame == VD_IMAGE_CONTENT_UNKNOWN && nImageToSame == VD_IMAGE_CONTENT_UNKNOWN)
8200 || (nImageFromSame != VD_IMAGE_CONTENT_UNKNOWN && nImageToSame != VD_IMAGE_CONTENT_UNKNOWN)),
8201 ("nImageFromSame=%u nImageToSame=%u\n", nImageFromSame, nImageToSame),
8202 rc = VERR_INVALID_PARAMETER);
8203
8204 /* Move the image. */
8205 if (pDiskFrom == pDiskTo)
8206 {
8207 /* Rename only works when backends are the same, are file based
8208 * and the rename method is implemented. */
8209 if ( fMoveByRename
8210 && !RTStrICmp(pszBackend, pImageFrom->Backend->pszBackendName)
8211 && pImageFrom->Backend->uBackendCaps & VD_CAP_FILE
8212 && pImageFrom->Backend->pfnRename)
8213 {
8214 rc2 = vdThreadFinishRead(pDiskFrom);
8215 AssertRC(rc2);
8216 fLockReadFrom = false;
8217
8218 rc2 = vdThreadStartWrite(pDiskFrom);
8219 AssertRC(rc2);
8220 fLockWriteFrom = true;
8221 rc = pImageFrom->Backend->pfnRename(pImageFrom->pBackendData, pszFilename ? pszFilename : pImageFrom->pszFilename);
8222 break;
8223 }
8224
8225 /** @todo Moving (including shrinking/growing) of the image is
8226 * requested, but the rename attempt failed or it wasn't possible.
8227 * Must now copy image to temp location. */
8228 AssertReleaseMsgFailed(("VDCopy: moving by copy/delete not implemented\n"));
8229 }
8230
8231 /* pszFilename is allowed to be NULL, as this indicates copy to the existing image. */
8232 AssertMsgBreakStmt(pszFilename == NULL || (VALID_PTR(pszFilename) && *pszFilename),
8233 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
8234 rc = VERR_INVALID_PARAMETER);
8235
8236 uint64_t cbSizeFrom;
8237 cbSizeFrom = pImageFrom->Backend->pfnGetSize(pImageFrom->pBackendData);
8238 if (cbSizeFrom == 0)
8239 {
8240 rc = VERR_VD_VALUE_NOT_FOUND;
8241 break;
8242 }
8243
8244 VDGEOMETRY PCHSGeometryFrom = {0, 0, 0};
8245 VDGEOMETRY LCHSGeometryFrom = {0, 0, 0};
8246 pImageFrom->Backend->pfnGetPCHSGeometry(pImageFrom->pBackendData, &PCHSGeometryFrom);
8247 pImageFrom->Backend->pfnGetLCHSGeometry(pImageFrom->pBackendData, &LCHSGeometryFrom);
8248
8249 RTUUID ImageUuid, ImageModificationUuid;
8250 if (pDiskFrom != pDiskTo)
8251 {
8252 if (pDstUuid)
8253 ImageUuid = *pDstUuid;
8254 else
8255 RTUuidCreate(&ImageUuid);
8256 }
8257 else
8258 {
8259 rc = pImageFrom->Backend->pfnGetUuid(pImageFrom->pBackendData, &ImageUuid);
8260 if (RT_FAILURE(rc))
8261 RTUuidCreate(&ImageUuid);
8262 }
8263 rc = pImageFrom->Backend->pfnGetModificationUuid(pImageFrom->pBackendData, &ImageModificationUuid);
8264 if (RT_FAILURE(rc))
8265 RTUuidClear(&ImageModificationUuid);
8266
8267 char szComment[1024];
8268 rc = pImageFrom->Backend->pfnGetComment(pImageFrom->pBackendData, szComment, sizeof(szComment));
8269 if (RT_FAILURE(rc))
8270 szComment[0] = '\0';
8271 else
8272 szComment[sizeof(szComment) - 1] = '\0';
8273
8274 rc2 = vdThreadFinishRead(pDiskFrom);
8275 AssertRC(rc2);
8276 fLockReadFrom = false;
8277
8278 rc2 = vdThreadStartRead(pDiskTo);
8279 AssertRC(rc2);
8280 unsigned cImagesTo = pDiskTo->cImages;
8281 rc2 = vdThreadFinishRead(pDiskTo);
8282 AssertRC(rc2);
8283
8284 if (pszFilename)
8285 {
8286 if (cbSize == 0)
8287 cbSize = cbSizeFrom;
8288
8289 /* Create destination image with the properties of source image. */
8290 /** @todo replace the VDCreateDiff/VDCreateBase calls by direct
8291 * calls to the backend. Unifies the code and reduces the API
8292 * dependencies. Would also make the synchronization explicit. */
8293 if (cImagesTo > 0)
8294 {
8295 rc = VDCreateDiff(pDiskTo, pszBackend, pszFilename,
8296 uImageFlags, szComment, &ImageUuid,
8297 NULL /* pParentUuid */,
8298 uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
8299 pDstVDIfsImage, NULL);
8300
8301 rc2 = vdThreadStartWrite(pDiskTo);
8302 AssertRC(rc2);
8303 fLockWriteTo = true;
8304 } else {
8305 /** @todo hack to force creation of a fixed image for
8306 * the RAW backend, which can't handle anything else. */
8307 if (!RTStrICmp(pszBackend, "RAW"))
8308 uImageFlags |= VD_IMAGE_FLAGS_FIXED;
8309
8310 vdFixupPCHSGeometry(&PCHSGeometryFrom, cbSize);
8311 vdFixupLCHSGeometry(&LCHSGeometryFrom, cbSize);
8312
8313 rc = VDCreateBase(pDiskTo, pszBackend, pszFilename, cbSize,
8314 uImageFlags, szComment,
8315 &PCHSGeometryFrom, &LCHSGeometryFrom,
8316 NULL, uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
8317 pDstVDIfsImage, NULL);
8318
8319 rc2 = vdThreadStartWrite(pDiskTo);
8320 AssertRC(rc2);
8321 fLockWriteTo = true;
8322
8323 if (RT_SUCCESS(rc) && !RTUuidIsNull(&ImageUuid))
8324 pDiskTo->pLast->Backend->pfnSetUuid(pDiskTo->pLast->pBackendData, &ImageUuid);
8325 }
8326 if (RT_FAILURE(rc))
8327 break;
8328
8329 pImageTo = pDiskTo->pLast;
8330 AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
8331
8332 cbSize = RT_MIN(cbSize, cbSizeFrom);
8333 }
8334 else
8335 {
8336 pImageTo = pDiskTo->pLast;
8337 AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
8338
8339 uint64_t cbSizeTo;
8340 cbSizeTo = pImageTo->Backend->pfnGetSize(pImageTo->pBackendData);
8341 if (cbSizeTo == 0)
8342 {
8343 rc = VERR_VD_VALUE_NOT_FOUND;
8344 break;
8345 }
8346
8347 if (cbSize == 0)
8348 cbSize = RT_MIN(cbSizeFrom, cbSizeTo);
8349
8350 vdFixupPCHSGeometry(&PCHSGeometryFrom, cbSize);
8351 vdFixupLCHSGeometry(&LCHSGeometryFrom, cbSize);
8352
8353 /* Update the geometry in the destination image. */
8354 pImageTo->Backend->pfnSetPCHSGeometry(pImageTo->pBackendData, &PCHSGeometryFrom);
8355 pImageTo->Backend->pfnSetLCHSGeometry(pImageTo->pBackendData, &LCHSGeometryFrom);
8356 }
8357
8358 rc2 = vdThreadFinishWrite(pDiskTo);
8359 AssertRC(rc2);
8360 fLockWriteTo = false;
8361
8362 /* Whether we can take the optimized copy path (false) or not.
8363 * Don't optimize if the image existed or if it is a child image. */
8364 bool fSuppressRedundantIo = ( !(pszFilename == NULL || cImagesTo > 0)
8365 || (nImageToSame != VD_IMAGE_CONTENT_UNKNOWN));
8366 unsigned cImagesFromReadBack, cImagesToReadBack;
8367
8368 if (nImageFromSame == VD_IMAGE_CONTENT_UNKNOWN)
8369 cImagesFromReadBack = 0;
8370 else
8371 {
8372 if (nImage == VD_LAST_IMAGE)
8373 cImagesFromReadBack = pDiskFrom->cImages - nImageFromSame - 1;
8374 else
8375 cImagesFromReadBack = nImage - nImageFromSame;
8376 }
8377
8378 if (nImageToSame == VD_IMAGE_CONTENT_UNKNOWN)
8379 cImagesToReadBack = 0;
8380 else
8381 cImagesToReadBack = pDiskTo->cImages - nImageToSame - 1;
8382
8383 /* Copy the data. */
8384 rc = vdCopyHelper(pDiskFrom, pImageFrom, pDiskTo, cbSize,
8385 cImagesFromReadBack, cImagesToReadBack,
8386 fSuppressRedundantIo, pIfProgress, pDstIfProgress);
8387
8388 if (RT_SUCCESS(rc))
8389 {
8390 rc2 = vdThreadStartWrite(pDiskTo);
8391 AssertRC(rc2);
8392 fLockWriteTo = true;
8393
8394 /* Only set modification UUID if it is non-null, since the source
8395 * backend might not provide a valid modification UUID. */
8396 if (!RTUuidIsNull(&ImageModificationUuid))
8397 pImageTo->Backend->pfnSetModificationUuid(pImageTo->pBackendData, &ImageModificationUuid);
8398
8399 /* Set the requested open flags if they differ from the value
8400 * required for creating the image and copying the contents. */
8401 if ( pImageTo && pszFilename
8402 && uOpenFlags != (uOpenFlags & ~VD_OPEN_FLAGS_READONLY))
8403 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
8404 uOpenFlags);
8405 }
8406 } while (0);
8407
8408 if (RT_FAILURE(rc) && pImageTo && pszFilename)
8409 {
8410 /* Take the write lock only if it is not taken. Not worth making the
8411 * above code even more complicated. */
8412 if (RT_UNLIKELY(!fLockWriteTo))
8413 {
8414 rc2 = vdThreadStartWrite(pDiskTo);
8415 AssertRC(rc2);
8416 fLockWriteTo = true;
8417 }
8418 /* Error detected, but new image created. Remove image from list. */
8419 vdRemoveImageFromList(pDiskTo, pImageTo);
8420
8421 /* Close and delete image. */
8422 rc2 = pImageTo->Backend->pfnClose(pImageTo->pBackendData, true);
8423 AssertRC(rc2);
8424 pImageTo->pBackendData = NULL;
8425
8426 /* Free remaining resources. */
8427 if (pImageTo->pszFilename)
8428 RTStrFree(pImageTo->pszFilename);
8429
8430 RTMemFree(pImageTo);
8431 }
8432
8433 if (RT_UNLIKELY(fLockWriteTo))
8434 {
8435 rc2 = vdThreadFinishWrite(pDiskTo);
8436 AssertRC(rc2);
8437 }
8438 if (RT_UNLIKELY(fLockWriteFrom))
8439 {
8440 rc2 = vdThreadFinishWrite(pDiskFrom);
8441 AssertRC(rc2);
8442 }
8443 else if (RT_UNLIKELY(fLockReadFrom))
8444 {
8445 rc2 = vdThreadFinishRead(pDiskFrom);
8446 AssertRC(rc2);
8447 }
8448
8449 if (RT_SUCCESS(rc))
8450 {
8451 if (pIfProgress && pIfProgress->pfnProgress)
8452 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
8453 if (pDstIfProgress && pDstIfProgress->pfnProgress)
8454 pDstIfProgress->pfnProgress(pDstIfProgress->Core.pvUser, 100);
8455 }
8456
8457 LogFlowFunc(("returns %Rrc\n", rc));
8458 return rc;
8459}
8460
8461/**
8462 * Copies an image from one HDD container to another.
8463 * The copy is opened in the target HDD container.
8464 * It is possible to convert between different image formats, because the
8465 * backend for the destination may be different from the source.
8466 * If both the source and destination reference the same HDD container,
8467 * then the image is moved (by copying/deleting or renaming) to the new location.
8468 * The source container is unchanged if the move operation fails, otherwise
8469 * the image at the new location is opened in the same way as the old one was.
8470 *
8471 * @returns VBox status code.
8472 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
8473 * @param pDiskFrom Pointer to source HDD container.
8474 * @param nImage Image number, counts from 0. 0 is always base image of container.
8475 * @param pDiskTo Pointer to destination HDD container.
8476 * @param pszBackend Name of the image file backend to use.
8477 * @param pszFilename New name of the image (may be NULL if pDiskFrom == pDiskTo).
8478 * @param fMoveByRename If true, attempt to perform a move by renaming (if successful the new size is ignored).
8479 * @param cbSize New image size (0 means leave unchanged).
8480 * @param uImageFlags Flags specifying special destination image features.
8481 * @param pDstUuid New UUID of the destination image. If NULL, a new UUID is created.
8482 * This parameter is used if and only if a true copy is created.
8483 * In all rename/move cases the UUIDs are copied over.
8484 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
8485 * Only used if the destination image is created.
8486 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
8487 * @param pDstVDIfsImage Pointer to the per-image VD interface list, for the
8488 * destination image.
8489 * @param pDstVDIfsOperation Pointer to the per-image VD interface list,
8490 * for the destination image.
8491 */
8492VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
8493 const char *pszBackend, const char *pszFilename,
8494 bool fMoveByRename, uint64_t cbSize,
8495 unsigned uImageFlags, PCRTUUID pDstUuid,
8496 unsigned uOpenFlags, PVDINTERFACE pVDIfsOperation,
8497 PVDINTERFACE pDstVDIfsImage,
8498 PVDINTERFACE pDstVDIfsOperation)
8499{
8500 return VDCopyEx(pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename,
8501 cbSize, VD_IMAGE_CONTENT_UNKNOWN, VD_IMAGE_CONTENT_UNKNOWN,
8502 uImageFlags, pDstUuid, uOpenFlags, pVDIfsOperation,
8503 pDstVDIfsImage, pDstVDIfsOperation);
8504}
8505
8506/**
8507 * Optimizes the storage consumption of an image. Typically the unused blocks
8508 * have to be wiped with zeroes to achieve a substantial reduced storage use.
8509 * Another optimization done is reordering the image blocks, which can provide
8510 * a significant performance boost, as reads and writes tend to use less random
8511 * file offsets.
8512 *
8513 * @return VBox status code.
8514 * @return VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
8515 * @return VERR_VD_IMAGE_READ_ONLY if image is not writable.
8516 * @return VERR_NOT_SUPPORTED if this kind of image can be compacted, but
8517 * the code for this isn't implemented yet.
8518 * @param pDisk Pointer to HDD container.
8519 * @param nImage Image number, counts from 0. 0 is always base image of container.
8520 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
8521 */
8522VBOXDDU_DECL(int) VDCompact(PVBOXHDD pDisk, unsigned nImage,
8523 PVDINTERFACE pVDIfsOperation)
8524{
8525 int rc = VINF_SUCCESS;
8526 int rc2;
8527 bool fLockRead = false, fLockWrite = false;
8528 void *pvBuf = NULL;
8529 void *pvTmp = NULL;
8530
8531 LogFlowFunc(("pDisk=%#p nImage=%u pVDIfsOperation=%#p\n",
8532 pDisk, nImage, pVDIfsOperation));
8533
8534 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
8535
8536 do {
8537 /* Check arguments. */
8538 AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
8539 rc = VERR_INVALID_PARAMETER);
8540 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE,
8541 ("u32Signature=%08x\n", pDisk->u32Signature));
8542
8543 rc2 = vdThreadStartRead(pDisk);
8544 AssertRC(rc2);
8545 fLockRead = true;
8546
8547 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8548 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
8549
8550 /* If there is no compact callback for not file based backends then
8551 * the backend doesn't need compaction. No need to make much fuss about
8552 * this. For file based ones signal this as not yet supported. */
8553 if (!pImage->Backend->pfnCompact)
8554 {
8555 if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
8556 rc = VERR_NOT_SUPPORTED;
8557 else
8558 rc = VINF_SUCCESS;
8559 break;
8560 }
8561
8562 /* Insert interface for reading parent state into per-operation list,
8563 * if there is a parent image. */
8564 VDINTERFACEPARENTSTATE VDIfParent;
8565 VDPARENTSTATEDESC ParentUser;
8566 if (pImage->pPrev)
8567 {
8568 VDIfParent.pfnParentRead = vdParentRead;
8569 ParentUser.pDisk = pDisk;
8570 ParentUser.pImage = pImage->pPrev;
8571 rc = VDInterfaceAdd(&VDIfParent.Core, "VDCompact_ParentState", VDINTERFACETYPE_PARENTSTATE,
8572 &ParentUser, sizeof(VDINTERFACEPARENTSTATE), &pVDIfsOperation);
8573 AssertRC(rc);
8574 }
8575
8576 rc2 = vdThreadFinishRead(pDisk);
8577 AssertRC(rc2);
8578 fLockRead = false;
8579
8580 rc2 = vdThreadStartWrite(pDisk);
8581 AssertRC(rc2);
8582 fLockWrite = true;
8583
8584 rc = pImage->Backend->pfnCompact(pImage->pBackendData,
8585 0, 99,
8586 pDisk->pVDIfsDisk,
8587 pImage->pVDIfsImage,
8588 pVDIfsOperation);
8589 } while (0);
8590
8591 if (RT_UNLIKELY(fLockWrite))
8592 {
8593 rc2 = vdThreadFinishWrite(pDisk);
8594 AssertRC(rc2);
8595 }
8596 else if (RT_UNLIKELY(fLockRead))
8597 {
8598 rc2 = vdThreadFinishRead(pDisk);
8599 AssertRC(rc2);
8600 }
8601
8602 if (pvBuf)
8603 RTMemTmpFree(pvBuf);
8604 if (pvTmp)
8605 RTMemTmpFree(pvTmp);
8606
8607 if (RT_SUCCESS(rc))
8608 {
8609 if (pIfProgress && pIfProgress->pfnProgress)
8610 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
8611 }
8612
8613 LogFlowFunc(("returns %Rrc\n", rc));
8614 return rc;
8615}
8616
8617/**
8618 * Resizes the given disk image to the given size.
8619 *
8620 * @return VBox status
8621 * @return VERR_VD_IMAGE_READ_ONLY if image is not writable.
8622 * @return VERR_NOT_SUPPORTED if this kind of image can be compacted, but
8623 *
8624 * @param pDisk Pointer to the HDD container.
8625 * @param cbSize New size of the image.
8626 * @param pPCHSGeometry Pointer to the new physical disk geometry <= (16383,16,63). Not NULL.
8627 * @param pLCHSGeometry Pointer to the new logical disk geometry <= (x,255,63). Not NULL.
8628 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
8629 */
8630VBOXDDU_DECL(int) VDResize(PVBOXHDD pDisk, uint64_t cbSize,
8631 PCVDGEOMETRY pPCHSGeometry,
8632 PCVDGEOMETRY pLCHSGeometry,
8633 PVDINTERFACE pVDIfsOperation)
8634{
8635 /** @todo r=klaus resizing was designed to be part of VDCopy, so having a separate function is not desirable. */
8636 int rc = VINF_SUCCESS;
8637 int rc2;
8638 bool fLockRead = false, fLockWrite = false;
8639
8640 LogFlowFunc(("pDisk=%#p cbSize=%llu pVDIfsOperation=%#p\n",
8641 pDisk, cbSize, pVDIfsOperation));
8642
8643 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
8644
8645 do {
8646 /* Check arguments. */
8647 AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
8648 rc = VERR_INVALID_PARAMETER);
8649 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE,
8650 ("u32Signature=%08x\n", pDisk->u32Signature));
8651
8652 rc2 = vdThreadStartRead(pDisk);
8653 AssertRC(rc2);
8654 fLockRead = true;
8655
8656 /* Must have at least one image in the chain, will resize last. */
8657 AssertMsgBreakStmt(pDisk->cImages >= 1, ("cImages=%u\n", pDisk->cImages),
8658 rc = VERR_NOT_SUPPORTED);
8659
8660 PVDIMAGE pImage = pDisk->pLast;
8661
8662 /* If there is no compact callback for not file based backends then
8663 * the backend doesn't need compaction. No need to make much fuss about
8664 * this. For file based ones signal this as not yet supported. */
8665 if (!pImage->Backend->pfnResize)
8666 {
8667 if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
8668 rc = VERR_NOT_SUPPORTED;
8669 else
8670 rc = VINF_SUCCESS;
8671 break;
8672 }
8673
8674 rc2 = vdThreadFinishRead(pDisk);
8675 AssertRC(rc2);
8676 fLockRead = false;
8677
8678 rc2 = vdThreadStartWrite(pDisk);
8679 AssertRC(rc2);
8680 fLockWrite = true;
8681
8682 VDGEOMETRY PCHSGeometryOld;
8683 VDGEOMETRY LCHSGeometryOld;
8684 PCVDGEOMETRY pPCHSGeometryNew;
8685 PCVDGEOMETRY pLCHSGeometryNew;
8686
8687 if (pPCHSGeometry->cCylinders == 0)
8688 {
8689 /* Auto-detect marker, calculate new value ourself. */
8690 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData, &PCHSGeometryOld);
8691 if (RT_SUCCESS(rc) && (PCHSGeometryOld.cCylinders != 0))
8692 PCHSGeometryOld.cCylinders = RT_MIN(cbSize / 512 / PCHSGeometryOld.cHeads / PCHSGeometryOld.cSectors, 16383);
8693 else if (rc == VERR_VD_GEOMETRY_NOT_SET)
8694 rc = VINF_SUCCESS;
8695
8696 pPCHSGeometryNew = &PCHSGeometryOld;
8697 }
8698 else
8699 pPCHSGeometryNew = pPCHSGeometry;
8700
8701 if (pLCHSGeometry->cCylinders == 0)
8702 {
8703 /* Auto-detect marker, calculate new value ourself. */
8704 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData, &LCHSGeometryOld);
8705 if (RT_SUCCESS(rc) && (LCHSGeometryOld.cCylinders != 0))
8706 LCHSGeometryOld.cCylinders = cbSize / 512 / LCHSGeometryOld.cHeads / LCHSGeometryOld.cSectors;
8707 else if (rc == VERR_VD_GEOMETRY_NOT_SET)
8708 rc = VINF_SUCCESS;
8709
8710 pLCHSGeometryNew = &LCHSGeometryOld;
8711 }
8712 else
8713 pLCHSGeometryNew = pLCHSGeometry;
8714
8715 if (RT_SUCCESS(rc))
8716 rc = pImage->Backend->pfnResize(pImage->pBackendData,
8717 cbSize,
8718 pPCHSGeometryNew,
8719 pLCHSGeometryNew,
8720 0, 99,
8721 pDisk->pVDIfsDisk,
8722 pImage->pVDIfsImage,
8723 pVDIfsOperation);
8724 } while (0);
8725
8726 if (RT_UNLIKELY(fLockWrite))
8727 {
8728 rc2 = vdThreadFinishWrite(pDisk);
8729 AssertRC(rc2);
8730 }
8731 else if (RT_UNLIKELY(fLockRead))
8732 {
8733 rc2 = vdThreadFinishRead(pDisk);
8734 AssertRC(rc2);
8735 }
8736
8737 if (RT_SUCCESS(rc))
8738 {
8739 if (pIfProgress && pIfProgress->pfnProgress)
8740 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
8741
8742 pDisk->cbSize = cbSize;
8743 }
8744
8745 LogFlowFunc(("returns %Rrc\n", rc));
8746 return rc;
8747}
8748
8749VBOXDDU_DECL(int) VDPrepareWithFilters(PVBOXHDD pDisk, PVDINTERFACE pVDIfsOperation)
8750{
8751 int rc = VINF_SUCCESS;
8752 int rc2;
8753 bool fLockRead = false, fLockWrite = false;
8754
8755 LogFlowFunc(("pDisk=%#p pVDIfsOperation=%#p\n", pDisk, pVDIfsOperation));
8756
8757 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
8758
8759 do {
8760 /* Check arguments. */
8761 AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
8762 rc = VERR_INVALID_PARAMETER);
8763 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE,
8764 ("u32Signature=%08x\n", pDisk->u32Signature));
8765
8766 rc2 = vdThreadStartRead(pDisk);
8767 AssertRC(rc2);
8768 fLockRead = true;
8769
8770 /* Must have at least one image in the chain. */
8771 AssertMsgBreakStmt(pDisk->cImages >= 1, ("cImages=%u\n", pDisk->cImages),
8772 rc = VERR_VD_NOT_OPENED);
8773
8774 unsigned uOpenFlags = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
8775 AssertMsgBreakStmt(!(uOpenFlags & VD_OPEN_FLAGS_READONLY),
8776 ("Last image should be read write"),
8777 rc = VERR_VD_IMAGE_READ_ONLY);
8778
8779 rc2 = vdThreadFinishRead(pDisk);
8780 AssertRC(rc2);
8781 fLockRead = false;
8782
8783 rc2 = vdThreadStartWrite(pDisk);
8784 AssertRC(rc2);
8785 fLockWrite = true;
8786
8787 /*
8788 * Open all images in the chain in read write mode first to avoid running
8789 * into an error in the middle of the process.
8790 */
8791 PVDIMAGE pImage = pDisk->pBase;
8792
8793 while (pImage)
8794 {
8795 uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
8796 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
8797 {
8798 /*
8799 * Clear skip consistency checks because the image is made writable now and
8800 * skipping consistency checks is only possible for readonly images.
8801 */
8802 uOpenFlags &= ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS);
8803 rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData, uOpenFlags);
8804 if (RT_FAILURE(rc))
8805 break;
8806 }
8807 pImage = pImage->pNext;
8808 }
8809
8810 if (RT_SUCCESS(rc))
8811 {
8812 unsigned cImgCur = 0;
8813 unsigned uPercentStart = 0;
8814 unsigned uPercentSpan = 100 / pDisk->cImages - 1;
8815
8816 /* Allocate tmp buffer. */
8817 void *pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
8818 if (!pvBuf)
8819 {
8820 rc = VERR_NO_MEMORY;
8821 break;
8822 }
8823
8824 pImage = pDisk->pBase;
8825 pDisk->fLocked = true;
8826
8827 while ( pImage
8828 && RT_SUCCESS(rc))
8829 {
8830 /* Get size of image. */
8831 uint64_t cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
8832 uint64_t cbSizeFile = pImage->Backend->pfnGetFileSize(pImage->pBackendData);
8833 uint64_t cbFileWritten = 0;
8834 uint64_t uOffset = 0;
8835 uint64_t cbRemaining = cbSize;
8836
8837 do
8838 {
8839 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
8840 RTSGSEG SegmentBuf;
8841 RTSGBUF SgBuf;
8842 VDIOCTX IoCtx;
8843
8844 SegmentBuf.pvSeg = pvBuf;
8845 SegmentBuf.cbSeg = VD_MERGE_BUFFER_SIZE;
8846 RTSgBufInit(&SgBuf, &SegmentBuf, 1);
8847 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_READ, 0, 0, NULL,
8848 &SgBuf, NULL, NULL, VDIOCTX_FLAGS_SYNC);
8849
8850 rc = pImage->Backend->pfnRead(pImage->pBackendData, uOffset,
8851 cbThisRead, &IoCtx, &cbThisRead);
8852 if (rc != VERR_VD_BLOCK_FREE)
8853 {
8854 if (RT_FAILURE(rc))
8855 break;
8856
8857 /* Apply filter chains. */
8858 rc = vdFilterChainApplyRead(pDisk, uOffset, cbThisRead, &IoCtx);
8859 if (RT_FAILURE(rc))
8860 break;
8861
8862 rc = vdFilterChainApplyWrite(pDisk, uOffset, cbThisRead, &IoCtx);
8863 if (RT_FAILURE(rc))
8864 break;
8865
8866 RTSgBufReset(&SgBuf);
8867 size_t cbThisWrite = 0;
8868 size_t cbPreRead = 0;
8869 size_t cbPostRead = 0;
8870 rc = pImage->Backend->pfnWrite(pImage->pBackendData, uOffset,
8871 cbThisRead, &IoCtx, &cbThisWrite,
8872 &cbPreRead, &cbPostRead, 0);
8873 if (RT_FAILURE(rc))
8874 break;
8875 Assert(cbThisWrite == cbThisRead);
8876 cbFileWritten += cbThisWrite;
8877 }
8878 else
8879 rc = VINF_SUCCESS;
8880
8881 uOffset += cbThisRead;
8882 cbRemaining -= cbThisRead;
8883
8884 if (pIfProgress && pIfProgress->pfnProgress)
8885 {
8886 rc2 = pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
8887 uPercentStart + cbFileWritten * uPercentSpan / cbSizeFile);
8888 AssertRC(rc2); /* Cancelling this operation without leaving an inconsistent state is not possible. */
8889 }
8890 } while (uOffset < cbSize);
8891
8892 pImage = pImage->pNext;
8893 cImgCur++;
8894 uPercentStart += uPercentSpan;
8895 }
8896
8897 pDisk->fLocked = false;
8898 if (pvBuf)
8899 RTMemTmpFree(pvBuf);
8900 }
8901
8902 /* Change images except last one back to readonly. */
8903 pImage = pDisk->pBase;
8904 while ( pImage != pDisk->pLast
8905 && pImage)
8906 {
8907 uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
8908 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
8909 rc2 = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData, uOpenFlags);
8910 if (RT_FAILURE(rc2))
8911 {
8912 if (RT_SUCCESS(rc))
8913 rc = rc2;
8914 break;
8915 }
8916 pImage = pImage->pNext;
8917 }
8918 } while (0);
8919
8920 if (RT_UNLIKELY(fLockWrite))
8921 {
8922 rc2 = vdThreadFinishWrite(pDisk);
8923 AssertRC(rc2);
8924 }
8925 else if (RT_UNLIKELY(fLockRead))
8926 {
8927 rc2 = vdThreadFinishRead(pDisk);
8928 AssertRC(rc2);
8929 }
8930
8931 if ( RT_SUCCESS(rc)
8932 && pIfProgress
8933 && pIfProgress->pfnProgress)
8934 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
8935
8936 LogFlowFunc(("returns %Rrc\n", rc));
8937 return rc;
8938}
8939
8940/**
8941 * Closes the last opened image file in HDD container.
8942 * If previous image file was opened in read-only mode (the normal case) and
8943 * the last opened image is in read-write mode then the previous image will be
8944 * reopened in read/write mode.
8945 *
8946 * @returns VBox status code.
8947 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
8948 * @param pDisk Pointer to HDD container.
8949 * @param fDelete If true, delete the image from the host disk.
8950 */
8951VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete)
8952{
8953 int rc = VINF_SUCCESS;
8954 int rc2;
8955 bool fLockWrite = false;
8956
8957 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
8958 do
8959 {
8960 /* sanity check */
8961 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8962 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8963
8964 /* Not worth splitting this up into a read lock phase and write
8965 * lock phase, as closing an image is a relatively fast operation
8966 * dominated by the part which needs the write lock. */
8967 rc2 = vdThreadStartWrite(pDisk);
8968 AssertRC(rc2);
8969 fLockWrite = true;
8970
8971 PVDIMAGE pImage = pDisk->pLast;
8972 if (!pImage)
8973 {
8974 rc = VERR_VD_NOT_OPENED;
8975 break;
8976 }
8977
8978 /* Destroy the current discard state first which might still have pending blocks. */
8979 rc = vdDiscardStateDestroy(pDisk);
8980 if (RT_FAILURE(rc))
8981 break;
8982
8983 unsigned uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
8984 /* Remove image from list of opened images. */
8985 vdRemoveImageFromList(pDisk, pImage);
8986 /* Close (and optionally delete) image. */
8987 rc = pImage->Backend->pfnClose(pImage->pBackendData, fDelete);
8988 /* Free remaining resources related to the image. */
8989 RTStrFree(pImage->pszFilename);
8990 RTMemFree(pImage);
8991
8992 pImage = pDisk->pLast;
8993 if (!pImage)
8994 break;
8995
8996 /* If disk was previously in read/write mode, make sure it will stay
8997 * like this (if possible) after closing this image. Set the open flags
8998 * accordingly. */
8999 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
9000 {
9001 uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
9002 uOpenFlags &= ~ VD_OPEN_FLAGS_READONLY;
9003 rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData, uOpenFlags);
9004 }
9005
9006 /* Cache disk information. */
9007 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
9008
9009 /* Cache PCHS geometry. */
9010 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
9011 &pDisk->PCHSGeometry);
9012 if (RT_FAILURE(rc2))
9013 {
9014 pDisk->PCHSGeometry.cCylinders = 0;
9015 pDisk->PCHSGeometry.cHeads = 0;
9016 pDisk->PCHSGeometry.cSectors = 0;
9017 }
9018 else
9019 {
9020 /* Make sure the PCHS geometry is properly clipped. */
9021 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
9022 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
9023 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
9024 }
9025
9026 /* Cache LCHS geometry. */
9027 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
9028 &pDisk->LCHSGeometry);
9029 if (RT_FAILURE(rc2))
9030 {
9031 pDisk->LCHSGeometry.cCylinders = 0;
9032 pDisk->LCHSGeometry.cHeads = 0;
9033 pDisk->LCHSGeometry.cSectors = 0;
9034 }
9035 else
9036 {
9037 /* Make sure the LCHS geometry is properly clipped. */
9038 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
9039 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
9040 }
9041 } while (0);
9042
9043 if (RT_UNLIKELY(fLockWrite))
9044 {
9045 rc2 = vdThreadFinishWrite(pDisk);
9046 AssertRC(rc2);
9047 }
9048
9049 LogFlowFunc(("returns %Rrc\n", rc));
9050 return rc;
9051}
9052
9053/**
9054 * Closes the currently opened cache image file in HDD container.
9055 *
9056 * @return VBox status code.
9057 * @return VERR_VD_NOT_OPENED if no cache is opened in HDD container.
9058 * @param pDisk Pointer to HDD container.
9059 * @param fDelete If true, delete the image from the host disk.
9060 */
9061VBOXDDU_DECL(int) VDCacheClose(PVBOXHDD pDisk, bool fDelete)
9062{
9063 int rc = VINF_SUCCESS;
9064 int rc2;
9065 bool fLockWrite = false;
9066 PVDCACHE pCache = NULL;
9067
9068 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
9069
9070 do
9071 {
9072 /* sanity check */
9073 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9074 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9075
9076 rc2 = vdThreadStartWrite(pDisk);
9077 AssertRC(rc2);
9078 fLockWrite = true;
9079
9080 AssertPtrBreakStmt(pDisk->pCache, rc = VERR_VD_CACHE_NOT_FOUND);
9081
9082 pCache = pDisk->pCache;
9083 pDisk->pCache = NULL;
9084
9085 pCache->Backend->pfnClose(pCache->pBackendData, fDelete);
9086 if (pCache->pszFilename)
9087 RTStrFree(pCache->pszFilename);
9088 RTMemFree(pCache);
9089 } while (0);
9090
9091 if (RT_LIKELY(fLockWrite))
9092 {
9093 rc2 = vdThreadFinishWrite(pDisk);
9094 AssertRC(rc2);
9095 }
9096
9097 LogFlowFunc(("returns %Rrc\n", rc));
9098 return rc;
9099}
9100
9101VBOXDDU_DECL(int) VDFilterRemove(PVBOXHDD pDisk, uint32_t fFlags)
9102{
9103 int rc = VINF_SUCCESS;
9104 int rc2;
9105 bool fLockWrite = false;
9106 PVDFILTER pFilter = NULL;
9107
9108 LogFlowFunc(("pDisk=%#p\n", pDisk));
9109
9110 do
9111 {
9112 /* sanity check */
9113 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9114 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9115
9116 AssertMsgBreakStmt(!(fFlags & ~VD_FILTER_FLAGS_MASK),
9117 ("Invalid flags set (fFlags=%#x)\n", fFlags),
9118 rc = VERR_INVALID_PARAMETER);
9119
9120 rc2 = vdThreadStartWrite(pDisk);
9121 AssertRC(rc2);
9122 fLockWrite = true;
9123
9124 if (fFlags & VD_FILTER_FLAGS_WRITE)
9125 {
9126 AssertBreakStmt(!RTListIsEmpty(&pDisk->ListFilterChainWrite), rc = VERR_VD_NOT_OPENED);
9127 pFilter = RTListGetLast(&pDisk->ListFilterChainWrite, VDFILTER, ListNodeChainWrite);
9128 AssertPtr(pFilter);
9129 RTListNodeRemove(&pFilter->ListNodeChainWrite);
9130 vdFilterRelease(pFilter);
9131 }
9132
9133 if (fFlags & VD_FILTER_FLAGS_READ)
9134 {
9135 AssertBreakStmt(!RTListIsEmpty(&pDisk->ListFilterChainRead), rc = VERR_VD_NOT_OPENED);
9136 pFilter = RTListGetLast(&pDisk->ListFilterChainRead, VDFILTER, ListNodeChainRead);
9137 AssertPtr(pFilter);
9138 RTListNodeRemove(&pFilter->ListNodeChainRead);
9139 vdFilterRelease(pFilter);
9140 }
9141 } while (0);
9142
9143 if (RT_LIKELY(fLockWrite))
9144 {
9145 rc2 = vdThreadFinishWrite(pDisk);
9146 AssertRC(rc2);
9147 }
9148
9149 LogFlowFunc(("returns %Rrc\n", rc));
9150 return rc;
9151}
9152
9153/**
9154 * Closes all opened image files in HDD container.
9155 *
9156 * @returns VBox status code.
9157 * @param pDisk Pointer to HDD container.
9158 */
9159VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk)
9160{
9161 int rc = VINF_SUCCESS;
9162 int rc2;
9163 bool fLockWrite = false;
9164
9165 LogFlowFunc(("pDisk=%#p\n", pDisk));
9166 do
9167 {
9168 /* sanity check */
9169 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9170 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9171
9172 /* Lock the entire operation. */
9173 rc2 = vdThreadStartWrite(pDisk);
9174 AssertRC(rc2);
9175 fLockWrite = true;
9176
9177 PVDCACHE pCache = pDisk->pCache;
9178 if (pCache)
9179 {
9180 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, false);
9181 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
9182 rc = rc2;
9183
9184 if (pCache->pszFilename)
9185 RTStrFree(pCache->pszFilename);
9186 RTMemFree(pCache);
9187 }
9188
9189 PVDIMAGE pImage = pDisk->pLast;
9190 while (VALID_PTR(pImage))
9191 {
9192 PVDIMAGE pPrev = pImage->pPrev;
9193 /* Remove image from list of opened images. */
9194 vdRemoveImageFromList(pDisk, pImage);
9195 /* Close image. */
9196 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, false);
9197 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
9198 rc = rc2;
9199 /* Free remaining resources related to the image. */
9200 RTStrFree(pImage->pszFilename);
9201 RTMemFree(pImage);
9202 pImage = pPrev;
9203 }
9204 Assert(!VALID_PTR(pDisk->pLast));
9205 } while (0);
9206
9207 if (RT_UNLIKELY(fLockWrite))
9208 {
9209 rc2 = vdThreadFinishWrite(pDisk);
9210 AssertRC(rc2);
9211 }
9212
9213 LogFlowFunc(("returns %Rrc\n", rc));
9214 return rc;
9215}
9216
9217/**
9218 * Removes all filters of the given HDD container.
9219 *
9220 * @return VBox status code.
9221 * @param pDisk Pointer to HDD container.
9222 */
9223VBOXDDU_DECL(int) VDFilterRemoveAll(PVBOXHDD pDisk)
9224{
9225 int rc = VINF_SUCCESS;
9226 int rc2;
9227 bool fLockWrite = false;
9228
9229 LogFlowFunc(("pDisk=%#p\n", pDisk));
9230 do
9231 {
9232 /* sanity check */
9233 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9234 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9235
9236 /* Lock the entire operation. */
9237 rc2 = vdThreadStartWrite(pDisk);
9238 AssertRC(rc2);
9239 fLockWrite = true;
9240
9241 PVDFILTER pFilter, pFilterNext;
9242 RTListForEachSafe(&pDisk->ListFilterChainWrite, pFilter, pFilterNext, VDFILTER, ListNodeChainWrite)
9243 {
9244 RTListNodeRemove(&pFilter->ListNodeChainWrite);
9245 vdFilterRelease(pFilter);
9246 }
9247
9248 RTListForEachSafe(&pDisk->ListFilterChainRead, pFilter, pFilterNext, VDFILTER, ListNodeChainRead)
9249 {
9250 RTListNodeRemove(&pFilter->ListNodeChainRead);
9251 vdFilterRelease(pFilter);
9252 }
9253 Assert(RTListIsEmpty(&pDisk->ListFilterChainRead));
9254 Assert(RTListIsEmpty(&pDisk->ListFilterChainWrite));
9255 } while (0);
9256
9257 if (RT_UNLIKELY(fLockWrite))
9258 {
9259 rc2 = vdThreadFinishWrite(pDisk);
9260 AssertRC(rc2);
9261 }
9262
9263 LogFlowFunc(("returns %Rrc\n", rc));
9264 return rc;
9265}
9266
9267/**
9268 * Read data from virtual HDD.
9269 *
9270 * @returns VBox status code.
9271 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
9272 * @param pDisk Pointer to HDD container.
9273 * @param uOffset Offset of first reading byte from start of disk.
9274 * @param pvBuf Pointer to buffer for reading data.
9275 * @param cbRead Number of bytes to read.
9276 */
9277VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuf,
9278 size_t cbRead)
9279{
9280 int rc = VINF_SUCCESS;
9281 int rc2;
9282 bool fLockRead = false;
9283
9284 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbRead=%zu\n",
9285 pDisk, uOffset, pvBuf, cbRead));
9286 do
9287 {
9288 /* sanity check */
9289 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9290 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9291
9292 /* Check arguments. */
9293 AssertMsgBreakStmt(VALID_PTR(pvBuf),
9294 ("pvBuf=%#p\n", pvBuf),
9295 rc = VERR_INVALID_PARAMETER);
9296 AssertMsgBreakStmt(cbRead,
9297 ("cbRead=%zu\n", cbRead),
9298 rc = VERR_INVALID_PARAMETER);
9299
9300 rc2 = vdThreadStartRead(pDisk);
9301 AssertRC(rc2);
9302 fLockRead = true;
9303
9304 PVDIMAGE pImage = pDisk->pLast;
9305 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
9306
9307 if (uOffset + cbRead > pDisk->cbSize)
9308 {
9309 /* Floppy images might be smaller than the standard expected by
9310 the floppy controller code. So, we won't fail here. */
9311 AssertMsgBreakStmt(pDisk->enmType == VDTYPE_FLOPPY,
9312 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
9313 uOffset, cbRead, pDisk->cbSize),
9314 rc = VERR_EOF);
9315 memset(pvBuf, 0xf6, cbRead); /* f6h = format.com filler byte */
9316 if (uOffset >= pDisk->cbSize)
9317 break;
9318 cbRead = pDisk->cbSize - uOffset;
9319 }
9320
9321 rc = vdReadHelper(pDisk, pImage, uOffset, pvBuf, cbRead,
9322 true /* fUpdateCache */);
9323 } while (0);
9324
9325 if (RT_UNLIKELY(fLockRead))
9326 {
9327 rc2 = vdThreadFinishRead(pDisk);
9328 AssertRC(rc2);
9329 }
9330
9331 LogFlowFunc(("returns %Rrc\n", rc));
9332 return rc;
9333}
9334
9335/**
9336 * Write data to virtual HDD.
9337 *
9338 * @returns VBox status code.
9339 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
9340 * @param pDisk Pointer to HDD container.
9341 * @param uOffset Offset of the first byte being
9342 * written from start of disk.
9343 * @param pvBuf Pointer to buffer for writing data.
9344 * @param cbWrite Number of bytes to write.
9345 */
9346VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf,
9347 size_t cbWrite)
9348{
9349 int rc = VINF_SUCCESS;
9350 int rc2;
9351 bool fLockWrite = false;
9352
9353 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbWrite=%zu\n",
9354 pDisk, uOffset, pvBuf, cbWrite));
9355 do
9356 {
9357 /* sanity check */
9358 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9359 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9360
9361 /* Check arguments. */
9362 AssertMsgBreakStmt(VALID_PTR(pvBuf),
9363 ("pvBuf=%#p\n", pvBuf),
9364 rc = VERR_INVALID_PARAMETER);
9365 AssertMsgBreakStmt(cbWrite,
9366 ("cbWrite=%zu\n", cbWrite),
9367 rc = VERR_INVALID_PARAMETER);
9368
9369 rc2 = vdThreadStartWrite(pDisk);
9370 AssertRC(rc2);
9371 fLockWrite = true;
9372
9373 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
9374 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
9375 uOffset, cbWrite, pDisk->cbSize),
9376 rc = VERR_INVALID_PARAMETER);
9377
9378 PVDIMAGE pImage = pDisk->pLast;
9379 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
9380
9381 vdSetModifiedFlag(pDisk);
9382 rc = vdWriteHelper(pDisk, pImage, uOffset, pvBuf, cbWrite,
9383 VDIOCTX_FLAGS_READ_UPDATE_CACHE);
9384 if (RT_FAILURE(rc))
9385 break;
9386
9387 /* If there is a merge (in the direction towards a parent) running
9388 * concurrently then we have to also "relay" the write to this parent,
9389 * as the merge position might be already past the position where
9390 * this write is going. The "context" of the write can come from the
9391 * natural chain, since merging either already did or will take care
9392 * of the "other" content which is might be needed to fill the block
9393 * to a full allocation size. The cache doesn't need to be touched
9394 * as this write is covered by the previous one. */
9395 if (RT_UNLIKELY(pDisk->pImageRelay))
9396 rc = vdWriteHelper(pDisk, pDisk->pImageRelay, uOffset,
9397 pvBuf, cbWrite, VDIOCTX_FLAGS_DEFAULT);
9398 } while (0);
9399
9400 if (RT_UNLIKELY(fLockWrite))
9401 {
9402 rc2 = vdThreadFinishWrite(pDisk);
9403 AssertRC(rc2);
9404 }
9405
9406 LogFlowFunc(("returns %Rrc\n", rc));
9407 return rc;
9408}
9409
9410/**
9411 * Make sure the on disk representation of a virtual HDD is up to date.
9412 *
9413 * @returns VBox status code.
9414 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
9415 * @param pDisk Pointer to HDD container.
9416 */
9417VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
9418{
9419 int rc = VINF_SUCCESS;
9420 int rc2;
9421 bool fLockWrite = false;
9422
9423 LogFlowFunc(("pDisk=%#p\n", pDisk));
9424 do
9425 {
9426 /* sanity check */
9427 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9428 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9429
9430 rc2 = vdThreadStartWrite(pDisk);
9431 AssertRC(rc2);
9432 fLockWrite = true;
9433
9434 PVDIMAGE pImage = pDisk->pLast;
9435 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
9436
9437 VDIOCTX IoCtx;
9438 RTSEMEVENT hEventComplete = NIL_RTSEMEVENT;
9439
9440 rc = RTSemEventCreate(&hEventComplete);
9441 if (RT_FAILURE(rc))
9442 break;
9443
9444 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_FLUSH, 0, 0, pImage, NULL,
9445 NULL, vdFlushHelperAsync, VDIOCTX_FLAGS_SYNC | VDIOCTX_FLAGS_DONT_FREE);
9446
9447 IoCtx.Type.Root.pfnComplete = vdIoCtxSyncComplete;
9448 IoCtx.Type.Root.pvUser1 = pDisk;
9449 IoCtx.Type.Root.pvUser2 = hEventComplete;
9450 rc = vdIoCtxProcessSync(&IoCtx, hEventComplete);
9451
9452 RTSemEventDestroy(hEventComplete);
9453 } while (0);
9454
9455 if (RT_UNLIKELY(fLockWrite))
9456 {
9457 rc2 = vdThreadFinishWrite(pDisk);
9458 AssertRC(rc2);
9459 }
9460
9461 LogFlowFunc(("returns %Rrc\n", rc));
9462 return rc;
9463}
9464
9465/**
9466 * Get number of opened images in HDD container.
9467 *
9468 * @returns Number of opened images for HDD container. 0 if no images have been opened.
9469 * @param pDisk Pointer to HDD container.
9470 */
9471VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk)
9472{
9473 unsigned cImages;
9474 int rc2;
9475 bool fLockRead = false;
9476
9477 LogFlowFunc(("pDisk=%#p\n", pDisk));
9478 do
9479 {
9480 /* sanity check */
9481 AssertPtrBreakStmt(pDisk, cImages = 0);
9482 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9483
9484 rc2 = vdThreadStartRead(pDisk);
9485 AssertRC(rc2);
9486 fLockRead = true;
9487
9488 cImages = pDisk->cImages;
9489 } while (0);
9490
9491 if (RT_UNLIKELY(fLockRead))
9492 {
9493 rc2 = vdThreadFinishRead(pDisk);
9494 AssertRC(rc2);
9495 }
9496
9497 LogFlowFunc(("returns %u\n", cImages));
9498 return cImages;
9499}
9500
9501/**
9502 * Get read/write mode of HDD container.
9503 *
9504 * @returns Virtual disk ReadOnly status.
9505 * @returns true if no image is opened in HDD container.
9506 * @param pDisk Pointer to HDD container.
9507 */
9508VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
9509{
9510 bool fReadOnly;
9511 int rc2;
9512 bool fLockRead = false;
9513
9514 LogFlowFunc(("pDisk=%#p\n", pDisk));
9515 do
9516 {
9517 /* sanity check */
9518 AssertPtrBreakStmt(pDisk, fReadOnly = false);
9519 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9520
9521 rc2 = vdThreadStartRead(pDisk);
9522 AssertRC(rc2);
9523 fLockRead = true;
9524
9525 PVDIMAGE pImage = pDisk->pLast;
9526 AssertPtrBreakStmt(pImage, fReadOnly = true);
9527
9528 unsigned uOpenFlags;
9529 uOpenFlags = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
9530 fReadOnly = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
9531 } while (0);
9532
9533 if (RT_UNLIKELY(fLockRead))
9534 {
9535 rc2 = vdThreadFinishRead(pDisk);
9536 AssertRC(rc2);
9537 }
9538
9539 LogFlowFunc(("returns %d\n", fReadOnly));
9540 return fReadOnly;
9541}
9542
9543/**
9544 * Get sector size of an image in HDD container.
9545 *
9546 * @return Virtual disk sector size in bytes.
9547 * @return 0 if image with specified number was not opened.
9548 * @param pDisk Pointer to HDD container.
9549 * @param nImage Image number, counts from 0. 0 is always base image of container.
9550 */
9551VBOXDDU_DECL(uint32_t) VDGetSectorSize(PVBOXHDD pDisk, unsigned nImage)
9552{
9553 uint64_t cbSector;
9554 int rc2;
9555 bool fLockRead = false;
9556
9557 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
9558 do
9559 {
9560 /* sanity check */
9561 AssertPtrBreakStmt(pDisk, cbSector = 0);
9562 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9563
9564 rc2 = vdThreadStartRead(pDisk);
9565 AssertRC(rc2);
9566 fLockRead = true;
9567
9568 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9569 AssertPtrBreakStmt(pImage, cbSector = 0);
9570 cbSector = pImage->Backend->pfnGetSectorSize(pImage->pBackendData);
9571 } while (0);
9572
9573 if (RT_UNLIKELY(fLockRead))
9574 {
9575 rc2 = vdThreadFinishRead(pDisk);
9576 AssertRC(rc2);
9577 }
9578
9579 LogFlowFunc(("returns %u\n", cbSector));
9580 return cbSector;
9581}
9582
9583/**
9584 * Get total capacity of an image in HDD container.
9585 *
9586 * @returns Virtual disk size in bytes.
9587 * @returns 0 if no image with specified number was not opened.
9588 * @param pDisk Pointer to HDD container.
9589 * @param nImage Image number, counts from 0. 0 is always base image of container.
9590 */
9591VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk, unsigned nImage)
9592{
9593 uint64_t cbSize;
9594 int rc2;
9595 bool fLockRead = false;
9596
9597 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
9598 do
9599 {
9600 /* sanity check */
9601 AssertPtrBreakStmt(pDisk, cbSize = 0);
9602 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9603
9604 rc2 = vdThreadStartRead(pDisk);
9605 AssertRC(rc2);
9606 fLockRead = true;
9607
9608 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9609 AssertPtrBreakStmt(pImage, cbSize = 0);
9610 cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
9611 } while (0);
9612
9613 if (RT_UNLIKELY(fLockRead))
9614 {
9615 rc2 = vdThreadFinishRead(pDisk);
9616 AssertRC(rc2);
9617 }
9618
9619 LogFlowFunc(("returns %llu\n", cbSize));
9620 return cbSize;
9621}
9622
9623/**
9624 * Get total file size of an image in HDD container.
9625 *
9626 * @returns Virtual disk size in bytes.
9627 * @returns 0 if no image is opened in HDD container.
9628 * @param pDisk Pointer to HDD container.
9629 * @param nImage Image number, counts from 0. 0 is always base image of container.
9630 */
9631VBOXDDU_DECL(uint64_t) VDGetFileSize(PVBOXHDD pDisk, unsigned nImage)
9632{
9633 uint64_t cbSize;
9634 int rc2;
9635 bool fLockRead = false;
9636
9637 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
9638 do
9639 {
9640 /* sanity check */
9641 AssertPtrBreakStmt(pDisk, cbSize = 0);
9642 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9643
9644 rc2 = vdThreadStartRead(pDisk);
9645 AssertRC(rc2);
9646 fLockRead = true;
9647
9648 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9649 AssertPtrBreakStmt(pImage, cbSize = 0);
9650 cbSize = pImage->Backend->pfnGetFileSize(pImage->pBackendData);
9651 } while (0);
9652
9653 if (RT_UNLIKELY(fLockRead))
9654 {
9655 rc2 = vdThreadFinishRead(pDisk);
9656 AssertRC(rc2);
9657 }
9658
9659 LogFlowFunc(("returns %llu\n", cbSize));
9660 return cbSize;
9661}
9662
9663/**
9664 * Get virtual disk PCHS geometry stored in HDD container.
9665 *
9666 * @returns VBox status code.
9667 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9668 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
9669 * @param pDisk Pointer to HDD container.
9670 * @param nImage Image number, counts from 0. 0 is always base image of container.
9671 * @param pPCHSGeometry Where to store PCHS geometry. Not NULL.
9672 */
9673VBOXDDU_DECL(int) VDGetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
9674 PVDGEOMETRY pPCHSGeometry)
9675{
9676 int rc = VINF_SUCCESS;
9677 int rc2;
9678 bool fLockRead = false;
9679
9680 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p\n",
9681 pDisk, nImage, pPCHSGeometry));
9682 do
9683 {
9684 /* sanity check */
9685 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9686 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9687
9688 /* Check arguments. */
9689 AssertMsgBreakStmt(VALID_PTR(pPCHSGeometry),
9690 ("pPCHSGeometry=%#p\n", pPCHSGeometry),
9691 rc = VERR_INVALID_PARAMETER);
9692
9693 rc2 = vdThreadStartRead(pDisk);
9694 AssertRC(rc2);
9695 fLockRead = true;
9696
9697 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9698 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9699
9700 if (pImage == pDisk->pLast)
9701 {
9702 /* Use cached information if possible. */
9703 if (pDisk->PCHSGeometry.cCylinders != 0)
9704 *pPCHSGeometry = pDisk->PCHSGeometry;
9705 else
9706 rc = VERR_VD_GEOMETRY_NOT_SET;
9707 }
9708 else
9709 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
9710 pPCHSGeometry);
9711 } while (0);
9712
9713 if (RT_UNLIKELY(fLockRead))
9714 {
9715 rc2 = vdThreadFinishRead(pDisk);
9716 AssertRC(rc2);
9717 }
9718
9719 LogFlowFunc(("%Rrc (PCHS=%u/%u/%u)\n", rc,
9720 pDisk->PCHSGeometry.cCylinders, pDisk->PCHSGeometry.cHeads,
9721 pDisk->PCHSGeometry.cSectors));
9722 return rc;
9723}
9724
9725/**
9726 * Store virtual disk PCHS geometry in HDD container.
9727 *
9728 * Note that in case of unrecoverable error all images in HDD container will be closed.
9729 *
9730 * @returns VBox status code.
9731 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9732 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
9733 * @param pDisk Pointer to HDD container.
9734 * @param nImage Image number, counts from 0. 0 is always base image of container.
9735 * @param pPCHSGeometry Where to load PCHS geometry from. Not NULL.
9736 */
9737VBOXDDU_DECL(int) VDSetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
9738 PCVDGEOMETRY pPCHSGeometry)
9739{
9740 int rc = VINF_SUCCESS;
9741 int rc2;
9742 bool fLockWrite = false;
9743
9744 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
9745 pDisk, nImage, pPCHSGeometry, pPCHSGeometry->cCylinders,
9746 pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
9747 do
9748 {
9749 /* sanity check */
9750 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9751 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9752
9753 /* Check arguments. */
9754 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
9755 && pPCHSGeometry->cHeads <= 16
9756 && pPCHSGeometry->cSectors <= 63,
9757 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
9758 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
9759 pPCHSGeometry->cSectors),
9760 rc = VERR_INVALID_PARAMETER);
9761
9762 rc2 = vdThreadStartWrite(pDisk);
9763 AssertRC(rc2);
9764 fLockWrite = true;
9765
9766 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9767 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9768
9769 if (pImage == pDisk->pLast)
9770 {
9771 if ( pPCHSGeometry->cCylinders != pDisk->PCHSGeometry.cCylinders
9772 || pPCHSGeometry->cHeads != pDisk->PCHSGeometry.cHeads
9773 || pPCHSGeometry->cSectors != pDisk->PCHSGeometry.cSectors)
9774 {
9775 /* Only update geometry if it is changed. Avoids similar checks
9776 * in every backend. Most of the time the new geometry is set
9777 * to the previous values, so no need to go through the hassle
9778 * of updating an image which could be opened in read-only mode
9779 * right now. */
9780 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pBackendData,
9781 pPCHSGeometry);
9782
9783 /* Cache new geometry values in any case. */
9784 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
9785 &pDisk->PCHSGeometry);
9786 if (RT_FAILURE(rc2))
9787 {
9788 pDisk->PCHSGeometry.cCylinders = 0;
9789 pDisk->PCHSGeometry.cHeads = 0;
9790 pDisk->PCHSGeometry.cSectors = 0;
9791 }
9792 else
9793 {
9794 /* Make sure the CHS geometry is properly clipped. */
9795 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 255);
9796 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
9797 }
9798 }
9799 }
9800 else
9801 {
9802 VDGEOMETRY PCHS;
9803 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
9804 &PCHS);
9805 if ( RT_FAILURE(rc)
9806 || pPCHSGeometry->cCylinders != PCHS.cCylinders
9807 || pPCHSGeometry->cHeads != PCHS.cHeads
9808 || pPCHSGeometry->cSectors != PCHS.cSectors)
9809 {
9810 /* Only update geometry if it is changed. Avoids similar checks
9811 * in every backend. Most of the time the new geometry is set
9812 * to the previous values, so no need to go through the hassle
9813 * of updating an image which could be opened in read-only mode
9814 * right now. */
9815 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pBackendData,
9816 pPCHSGeometry);
9817 }
9818 }
9819 } while (0);
9820
9821 if (RT_UNLIKELY(fLockWrite))
9822 {
9823 rc2 = vdThreadFinishWrite(pDisk);
9824 AssertRC(rc2);
9825 }
9826
9827 LogFlowFunc(("returns %Rrc\n", rc));
9828 return rc;
9829}
9830
9831/**
9832 * Get virtual disk LCHS geometry stored in HDD container.
9833 *
9834 * @returns VBox status code.
9835 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9836 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
9837 * @param pDisk Pointer to HDD container.
9838 * @param nImage Image number, counts from 0. 0 is always base image of container.
9839 * @param pLCHSGeometry Where to store LCHS geometry. Not NULL.
9840 */
9841VBOXDDU_DECL(int) VDGetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
9842 PVDGEOMETRY pLCHSGeometry)
9843{
9844 int rc = VINF_SUCCESS;
9845 int rc2;
9846 bool fLockRead = false;
9847
9848 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p\n",
9849 pDisk, nImage, pLCHSGeometry));
9850 do
9851 {
9852 /* sanity check */
9853 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9854 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9855
9856 /* Check arguments. */
9857 AssertMsgBreakStmt(VALID_PTR(pLCHSGeometry),
9858 ("pLCHSGeometry=%#p\n", pLCHSGeometry),
9859 rc = VERR_INVALID_PARAMETER);
9860
9861 rc2 = vdThreadStartRead(pDisk);
9862 AssertRC(rc2);
9863 fLockRead = true;
9864
9865 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9866 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9867
9868 if (pImage == pDisk->pLast)
9869 {
9870 /* Use cached information if possible. */
9871 if (pDisk->LCHSGeometry.cCylinders != 0)
9872 *pLCHSGeometry = pDisk->LCHSGeometry;
9873 else
9874 rc = VERR_VD_GEOMETRY_NOT_SET;
9875 }
9876 else
9877 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
9878 pLCHSGeometry);
9879 } while (0);
9880
9881 if (RT_UNLIKELY(fLockRead))
9882 {
9883 rc2 = vdThreadFinishRead(pDisk);
9884 AssertRC(rc2);
9885 }
9886
9887 LogFlowFunc((": %Rrc (LCHS=%u/%u/%u)\n", rc,
9888 pDisk->LCHSGeometry.cCylinders, pDisk->LCHSGeometry.cHeads,
9889 pDisk->LCHSGeometry.cSectors));
9890 return rc;
9891}
9892
9893/**
9894 * Store virtual disk LCHS geometry in HDD container.
9895 *
9896 * Note that in case of unrecoverable error all images in HDD container will be closed.
9897 *
9898 * @returns VBox status code.
9899 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9900 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
9901 * @param pDisk Pointer to HDD container.
9902 * @param nImage Image number, counts from 0. 0 is always base image of container.
9903 * @param pLCHSGeometry Where to load LCHS geometry from. Not NULL.
9904 */
9905VBOXDDU_DECL(int) VDSetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
9906 PCVDGEOMETRY pLCHSGeometry)
9907{
9908 int rc = VINF_SUCCESS;
9909 int rc2;
9910 bool fLockWrite = false;
9911
9912 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
9913 pDisk, nImage, pLCHSGeometry, pLCHSGeometry->cCylinders,
9914 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
9915 do
9916 {
9917 /* sanity check */
9918 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9919 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9920
9921 /* Check arguments. */
9922 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
9923 && pLCHSGeometry->cHeads <= 255
9924 && pLCHSGeometry->cSectors <= 63,
9925 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
9926 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
9927 pLCHSGeometry->cSectors),
9928 rc = VERR_INVALID_PARAMETER);
9929
9930 rc2 = vdThreadStartWrite(pDisk);
9931 AssertRC(rc2);
9932 fLockWrite = true;
9933
9934 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9935 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9936
9937 if (pImage == pDisk->pLast)
9938 {
9939 if ( pLCHSGeometry->cCylinders != pDisk->LCHSGeometry.cCylinders
9940 || pLCHSGeometry->cHeads != pDisk->LCHSGeometry.cHeads
9941 || pLCHSGeometry->cSectors != pDisk->LCHSGeometry.cSectors)
9942 {
9943 /* Only update geometry if it is changed. Avoids similar checks
9944 * in every backend. Most of the time the new geometry is set
9945 * to the previous values, so no need to go through the hassle
9946 * of updating an image which could be opened in read-only mode
9947 * right now. */
9948 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pBackendData,
9949 pLCHSGeometry);
9950
9951 /* Cache new geometry values in any case. */
9952 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
9953 &pDisk->LCHSGeometry);
9954 if (RT_FAILURE(rc2))
9955 {
9956 pDisk->LCHSGeometry.cCylinders = 0;
9957 pDisk->LCHSGeometry.cHeads = 0;
9958 pDisk->LCHSGeometry.cSectors = 0;
9959 }
9960 else
9961 {
9962 /* Make sure the CHS geometry is properly clipped. */
9963 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
9964 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
9965 }
9966 }
9967 }
9968 else
9969 {
9970 VDGEOMETRY LCHS;
9971 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
9972 &LCHS);
9973 if ( RT_FAILURE(rc)
9974 || pLCHSGeometry->cCylinders != LCHS.cCylinders
9975 || pLCHSGeometry->cHeads != LCHS.cHeads
9976 || pLCHSGeometry->cSectors != LCHS.cSectors)
9977 {
9978 /* Only update geometry if it is changed. Avoids similar checks
9979 * in every backend. Most of the time the new geometry is set
9980 * to the previous values, so no need to go through the hassle
9981 * of updating an image which could be opened in read-only mode
9982 * right now. */
9983 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pBackendData,
9984 pLCHSGeometry);
9985 }
9986 }
9987 } while (0);
9988
9989 if (RT_UNLIKELY(fLockWrite))
9990 {
9991 rc2 = vdThreadFinishWrite(pDisk);
9992 AssertRC(rc2);
9993 }
9994
9995 LogFlowFunc(("returns %Rrc\n", rc));
9996 return rc;
9997}
9998
9999/**
10000 * Get version of image in HDD container.
10001 *
10002 * @returns VBox status code.
10003 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
10004 * @param pDisk Pointer to HDD container.
10005 * @param nImage Image number, counts from 0. 0 is always base image of container.
10006 * @param puVersion Where to store the image version.
10007 */
10008VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
10009 unsigned *puVersion)
10010{
10011 int rc = VINF_SUCCESS;
10012 int rc2;
10013 bool fLockRead = false;
10014
10015 LogFlowFunc(("pDisk=%#p nImage=%u puVersion=%#p\n",
10016 pDisk, nImage, puVersion));
10017 do
10018 {
10019 /* sanity check */
10020 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10021 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10022
10023 /* Check arguments. */
10024 AssertMsgBreakStmt(VALID_PTR(puVersion),
10025 ("puVersion=%#p\n", puVersion),
10026 rc = VERR_INVALID_PARAMETER);
10027
10028 rc2 = vdThreadStartRead(pDisk);
10029 AssertRC(rc2);
10030 fLockRead = true;
10031
10032 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
10033 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
10034
10035 *puVersion = pImage->Backend->pfnGetVersion(pImage->pBackendData);
10036 } while (0);
10037
10038 if (RT_UNLIKELY(fLockRead))
10039 {
10040 rc2 = vdThreadFinishRead(pDisk);
10041 AssertRC(rc2);
10042 }
10043
10044 LogFlowFunc(("returns %Rrc uVersion=%#x\n", rc, *puVersion));
10045 return rc;
10046}
10047
10048/**
10049 * List the capabilities of image backend in HDD container.
10050 *
10051 * @returns VBox status code.
10052 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
10053 * @param pDisk Pointer to the HDD container.
10054 * @param nImage Image number, counts from 0. 0 is always base image of container.
10055 * @param pbackendInfo Where to store the backend information.
10056 */
10057VBOXDDU_DECL(int) VDBackendInfoSingle(PVBOXHDD pDisk, unsigned nImage,
10058 PVDBACKENDINFO pBackendInfo)
10059{
10060 int rc = VINF_SUCCESS;
10061 int rc2;
10062 bool fLockRead = false;
10063
10064 LogFlowFunc(("pDisk=%#p nImage=%u pBackendInfo=%#p\n",
10065 pDisk, nImage, pBackendInfo));
10066 do
10067 {
10068 /* sanity check */
10069 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10070 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10071
10072 /* Check arguments. */
10073 AssertMsgBreakStmt(VALID_PTR(pBackendInfo),
10074 ("pBackendInfo=%#p\n", pBackendInfo),
10075 rc = VERR_INVALID_PARAMETER);
10076
10077 rc2 = vdThreadStartRead(pDisk);
10078 AssertRC(rc2);
10079 fLockRead = true;
10080
10081 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
10082 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
10083
10084 pBackendInfo->pszBackend = pImage->Backend->pszBackendName;
10085 pBackendInfo->uBackendCaps = pImage->Backend->uBackendCaps;
10086 pBackendInfo->paFileExtensions = pImage->Backend->paFileExtensions;
10087 pBackendInfo->paConfigInfo = pImage->Backend->paConfigInfo;
10088 } while (0);
10089
10090 if (RT_UNLIKELY(fLockRead))
10091 {
10092 rc2 = vdThreadFinishRead(pDisk);
10093 AssertRC(rc2);
10094 }
10095
10096 LogFlowFunc(("returns %Rrc\n", rc));
10097 return rc;
10098}
10099
10100/**
10101 * Get flags of image in HDD container.
10102 *
10103 * @returns VBox status code.
10104 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
10105 * @param pDisk Pointer to HDD container.
10106 * @param nImage Image number, counts from 0. 0 is always base image of container.
10107 * @param puImageFlags Where to store the image flags.
10108 */
10109VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage,
10110 unsigned *puImageFlags)
10111{
10112 int rc = VINF_SUCCESS;
10113 int rc2;
10114 bool fLockRead = false;
10115
10116 LogFlowFunc(("pDisk=%#p nImage=%u puImageFlags=%#p\n",
10117 pDisk, nImage, puImageFlags));
10118 do
10119 {
10120 /* sanity check */
10121 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10122 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10123
10124 /* Check arguments. */
10125 AssertMsgBreakStmt(VALID_PTR(puImageFlags),
10126 ("puImageFlags=%#p\n", puImageFlags),
10127 rc = VERR_INVALID_PARAMETER);
10128
10129 rc2 = vdThreadStartRead(pDisk);
10130 AssertRC(rc2);
10131 fLockRead = true;
10132
10133 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
10134 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
10135
10136 *puImageFlags = pImage->uImageFlags;
10137 } while (0);
10138
10139 if (RT_UNLIKELY(fLockRead))
10140 {
10141 rc2 = vdThreadFinishRead(pDisk);
10142 AssertRC(rc2);
10143 }
10144
10145 LogFlowFunc(("returns %Rrc uImageFlags=%#x\n", rc, *puImageFlags));
10146 return rc;
10147}
10148
10149/**
10150 * Get open flags of image in HDD container.
10151 *
10152 * @returns VBox status code.
10153 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
10154 * @param pDisk Pointer to HDD container.
10155 * @param nImage Image number, counts from 0. 0 is always base image of container.
10156 * @param puOpenFlags Where to store the image open flags.
10157 */
10158VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
10159 unsigned *puOpenFlags)
10160{
10161 int rc = VINF_SUCCESS;
10162 int rc2;
10163 bool fLockRead = false;
10164
10165 LogFlowFunc(("pDisk=%#p nImage=%u puOpenFlags=%#p\n",
10166 pDisk, nImage, puOpenFlags));
10167 do
10168 {
10169 /* sanity check */
10170 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10171 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10172
10173 /* Check arguments. */
10174 AssertMsgBreakStmt(VALID_PTR(puOpenFlags),
10175 ("puOpenFlags=%#p\n", puOpenFlags),
10176 rc = VERR_INVALID_PARAMETER);
10177
10178 rc2 = vdThreadStartRead(pDisk);
10179 AssertRC(rc2);
10180 fLockRead = true;
10181
10182 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
10183 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
10184
10185 *puOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
10186 } while (0);
10187
10188 if (RT_UNLIKELY(fLockRead))
10189 {
10190 rc2 = vdThreadFinishRead(pDisk);
10191 AssertRC(rc2);
10192 }
10193
10194 LogFlowFunc(("returns %Rrc uOpenFlags=%#x\n", rc, *puOpenFlags));
10195 return rc;
10196}
10197
10198/**
10199 * Set open flags of image in HDD container.
10200 * This operation may cause file locking changes and/or files being reopened.
10201 * Note that in case of unrecoverable error all images in HDD container will be closed.
10202 *
10203 * @returns VBox status code.
10204 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
10205 * @param pDisk Pointer to HDD container.
10206 * @param nImage Image number, counts from 0. 0 is always base image of container.
10207 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
10208 */
10209VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
10210 unsigned uOpenFlags)
10211{
10212 int rc;
10213 int rc2;
10214 bool fLockWrite = false;
10215
10216 LogFlowFunc(("pDisk=%#p uOpenFlags=%#u\n", pDisk, uOpenFlags));
10217 do
10218 {
10219 /* sanity check */
10220 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10221 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10222
10223 /* Check arguments. */
10224 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
10225 ("uOpenFlags=%#x\n", uOpenFlags),
10226 rc = VERR_INVALID_PARAMETER);
10227
10228 rc2 = vdThreadStartWrite(pDisk);
10229 AssertRC(rc2);
10230 fLockWrite = true;
10231
10232 /* Destroy any discard state because the image might be changed to readonly mode. */
10233 rc = vdDiscardStateDestroy(pDisk);
10234 if (RT_FAILURE(rc))
10235 break;
10236
10237 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
10238 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
10239
10240 rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData,
10241 uOpenFlags & ~(VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_IGNORE_FLUSH | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS));
10242 if (RT_SUCCESS(rc))
10243 pImage->uOpenFlags = uOpenFlags & (VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_DISCARD | VD_OPEN_FLAGS_IGNORE_FLUSH | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS);
10244 } while (0);
10245
10246 if (RT_UNLIKELY(fLockWrite))
10247 {
10248 rc2 = vdThreadFinishWrite(pDisk);
10249 AssertRC(rc2);
10250 }
10251
10252 LogFlowFunc(("returns %Rrc\n", rc));
10253 return rc;
10254}
10255
10256/**
10257 * Get base filename of image in HDD container. Some image formats use
10258 * other filenames as well, so don't use this for anything but informational
10259 * purposes.
10260 *
10261 * @returns VBox status code.
10262 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
10263 * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
10264 * @param pDisk Pointer to HDD container.
10265 * @param nImage Image number, counts from 0. 0 is always base image of container.
10266 * @param pszFilename Where to store the image file name.
10267 * @param cbFilename Size of buffer pszFilename points to.
10268 */
10269VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
10270 char *pszFilename, unsigned cbFilename)
10271{
10272 int rc;
10273 int rc2;
10274 bool fLockRead = false;
10275
10276 LogFlowFunc(("pDisk=%#p nImage=%u pszFilename=%#p cbFilename=%u\n",
10277 pDisk, nImage, pszFilename, cbFilename));
10278 do
10279 {
10280 /* sanity check */
10281 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10282 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10283
10284 /* Check arguments. */
10285 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
10286 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
10287 rc = VERR_INVALID_PARAMETER);
10288 AssertMsgBreakStmt(cbFilename,
10289 ("cbFilename=%u\n", cbFilename),
10290 rc = VERR_INVALID_PARAMETER);
10291
10292 rc2 = vdThreadStartRead(pDisk);
10293 AssertRC(rc2);
10294 fLockRead = true;
10295
10296 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
10297 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
10298
10299 size_t cb = strlen(pImage->pszFilename);
10300 if (cb <= cbFilename)
10301 {
10302 strcpy(pszFilename, pImage->pszFilename);
10303 rc = VINF_SUCCESS;
10304 }
10305 else
10306 {
10307 strncpy(pszFilename, pImage->pszFilename, cbFilename - 1);
10308 pszFilename[cbFilename - 1] = '\0';
10309 rc = VERR_BUFFER_OVERFLOW;
10310 }
10311 } while (0);
10312
10313 if (RT_UNLIKELY(fLockRead))
10314 {
10315 rc2 = vdThreadFinishRead(pDisk);
10316 AssertRC(rc2);
10317 }
10318
10319 LogFlowFunc(("returns %Rrc, pszFilename=\"%s\"\n", rc, pszFilename));
10320 return rc;
10321}
10322
10323/**
10324 * Get the comment line of image in HDD container.
10325 *
10326 * @returns VBox status code.
10327 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
10328 * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
10329 * @param pDisk Pointer to HDD container.
10330 * @param nImage Image number, counts from 0. 0 is always base image of container.
10331 * @param pszComment Where to store the comment string of image. NULL is ok.
10332 * @param cbComment The size of pszComment buffer. 0 is ok.
10333 */
10334VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
10335 char *pszComment, unsigned cbComment)
10336{
10337 int rc;
10338 int rc2;
10339 bool fLockRead = false;
10340
10341 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p cbComment=%u\n",
10342 pDisk, nImage, pszComment, cbComment));
10343 do
10344 {
10345 /* sanity check */
10346 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10347 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10348
10349 /* Check arguments. */
10350 AssertMsgBreakStmt(VALID_PTR(pszComment),
10351 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
10352 rc = VERR_INVALID_PARAMETER);
10353 AssertMsgBreakStmt(cbComment,
10354 ("cbComment=%u\n", cbComment),
10355 rc = VERR_INVALID_PARAMETER);
10356
10357 rc2 = vdThreadStartRead(pDisk);
10358 AssertRC(rc2);
10359 fLockRead = true;
10360
10361 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
10362 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
10363
10364 rc = pImage->Backend->pfnGetComment(pImage->pBackendData, pszComment,
10365 cbComment);
10366 } while (0);
10367
10368 if (RT_UNLIKELY(fLockRead))
10369 {
10370 rc2 = vdThreadFinishRead(pDisk);
10371 AssertRC(rc2);
10372 }
10373
10374 LogFlowFunc(("returns %Rrc, pszComment=\"%s\"\n", rc, pszComment));
10375 return rc;
10376}
10377
10378/**
10379 * Changes the comment line of image in HDD container.
10380 *
10381 * @returns VBox status code.
10382 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
10383 * @param pDisk Pointer to HDD container.
10384 * @param nImage Image number, counts from 0. 0 is always base image of container.
10385 * @param pszComment New comment string (UTF-8). NULL is allowed to reset the comment.
10386 */
10387VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
10388 const char *pszComment)
10389{
10390 int rc;
10391 int rc2;
10392 bool fLockWrite = false;
10393
10394 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p \"%s\"\n",
10395 pDisk, nImage, pszComment, pszComment));
10396 do
10397 {
10398 /* sanity check */
10399 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10400 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10401
10402 /* Check arguments. */
10403 AssertMsgBreakStmt(VALID_PTR(pszComment) || pszComment == NULL,
10404 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
10405 rc = VERR_INVALID_PARAMETER);
10406
10407 rc2 = vdThreadStartWrite(pDisk);
10408 AssertRC(rc2);
10409 fLockWrite = true;
10410
10411 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
10412 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
10413
10414 rc = pImage->Backend->pfnSetComment(pImage->pBackendData, pszComment);
10415 } while (0);
10416
10417 if (RT_UNLIKELY(fLockWrite))
10418 {
10419 rc2 = vdThreadFinishWrite(pDisk);
10420 AssertRC(rc2);
10421 }
10422
10423 LogFlowFunc(("returns %Rrc\n", rc));
10424 return rc;
10425}
10426
10427
10428/**
10429 * Get UUID of image in HDD container.
10430 *
10431 * @returns VBox status code.
10432 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
10433 * @param pDisk Pointer to HDD container.
10434 * @param nImage Image number, counts from 0. 0 is always base image of container.
10435 * @param pUuid Where to store the image creation UUID.
10436 */
10437VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
10438{
10439 int rc;
10440 int rc2;
10441 bool fLockRead = false;
10442
10443 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
10444 do
10445 {
10446 /* sanity check */
10447 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10448 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10449
10450 /* Check arguments. */
10451 AssertMsgBreakStmt(VALID_PTR(pUuid),
10452 ("pUuid=%#p\n", pUuid),
10453 rc = VERR_INVALID_PARAMETER);
10454
10455 rc2 = vdThreadStartRead(pDisk);
10456 AssertRC(rc2);
10457 fLockRead = true;
10458
10459 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
10460 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
10461
10462 rc = pImage->Backend->pfnGetUuid(pImage->pBackendData, pUuid);
10463 } while (0);
10464
10465 if (RT_UNLIKELY(fLockRead))
10466 {
10467 rc2 = vdThreadFinishRead(pDisk);
10468 AssertRC(rc2);
10469 }
10470
10471 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
10472 return rc;
10473}
10474
10475/**
10476 * Set the image's UUID. Should not be used by normal applications.
10477 *
10478 * @returns VBox status code.
10479 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
10480 * @param pDisk Pointer to HDD container.
10481 * @param nImage Image number, counts from 0. 0 is always base image of container.
10482 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
10483 */
10484VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
10485{
10486 int rc;
10487 int rc2;
10488 bool fLockWrite = false;
10489
10490 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
10491 pDisk, nImage, pUuid, pUuid));
10492 do
10493 {
10494 /* sanity check */
10495 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10496 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10497
10498 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
10499 ("pUuid=%#p\n", pUuid),
10500 rc = VERR_INVALID_PARAMETER);
10501
10502 rc2 = vdThreadStartWrite(pDisk);
10503 AssertRC(rc2);
10504 fLockWrite = true;
10505
10506 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
10507 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
10508
10509 RTUUID Uuid;
10510 if (!pUuid)
10511 {
10512 RTUuidCreate(&Uuid);
10513 pUuid = &Uuid;
10514 }
10515 rc = pImage->Backend->pfnSetUuid(pImage->pBackendData, pUuid);
10516 } while (0);
10517
10518 if (RT_UNLIKELY(fLockWrite))
10519 {
10520 rc2 = vdThreadFinishWrite(pDisk);
10521 AssertRC(rc2);
10522 }
10523
10524 LogFlowFunc(("returns %Rrc\n", rc));
10525 return rc;
10526}
10527
10528/**
10529 * Get last modification UUID of image in HDD container.
10530 *
10531 * @returns VBox status code.
10532 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
10533 * @param pDisk Pointer to HDD container.
10534 * @param nImage Image number, counts from 0. 0 is always base image of container.
10535 * @param pUuid Where to store the image modification UUID.
10536 */
10537VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
10538{
10539 int rc = VINF_SUCCESS;
10540 int rc2;
10541 bool fLockRead = false;
10542
10543 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
10544 do
10545 {
10546 /* sanity check */
10547 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10548 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10549
10550 /* Check arguments. */
10551 AssertMsgBreakStmt(VALID_PTR(pUuid),
10552 ("pUuid=%#p\n", pUuid),
10553 rc = VERR_INVALID_PARAMETER);
10554
10555 rc2 = vdThreadStartRead(pDisk);
10556 AssertRC(rc2);
10557 fLockRead = true;
10558
10559 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
10560 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
10561
10562 rc = pImage->Backend->pfnGetModificationUuid(pImage->pBackendData,
10563 pUuid);
10564 } while (0);
10565
10566 if (RT_UNLIKELY(fLockRead))
10567 {
10568 rc2 = vdThreadFinishRead(pDisk);
10569 AssertRC(rc2);
10570 }
10571
10572 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
10573 return rc;
10574}
10575
10576/**
10577 * Set the image's last modification UUID. Should not be used by normal applications.
10578 *
10579 * @returns VBox status code.
10580 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
10581 * @param pDisk Pointer to HDD container.
10582 * @param nImage Image number, counts from 0. 0 is always base image of container.
10583 * @param pUuid New modification UUID of the image. If NULL, a new UUID is created.
10584 */
10585VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
10586{
10587 int rc;
10588 int rc2;
10589 bool fLockWrite = false;
10590
10591 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
10592 pDisk, nImage, pUuid, pUuid));
10593 do
10594 {
10595 /* sanity check */
10596 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10597 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10598
10599 /* Check arguments. */
10600 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
10601 ("pUuid=%#p\n", pUuid),
10602 rc = VERR_INVALID_PARAMETER);
10603
10604 rc2 = vdThreadStartWrite(pDisk);
10605 AssertRC(rc2);
10606 fLockWrite = true;
10607
10608 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
10609 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
10610
10611 RTUUID Uuid;
10612 if (!pUuid)
10613 {
10614 RTUuidCreate(&Uuid);
10615 pUuid = &Uuid;
10616 }
10617 rc = pImage->Backend->pfnSetModificationUuid(pImage->pBackendData,
10618 pUuid);
10619 } while (0);
10620
10621 if (RT_UNLIKELY(fLockWrite))
10622 {
10623 rc2 = vdThreadFinishWrite(pDisk);
10624 AssertRC(rc2);
10625 }
10626
10627 LogFlowFunc(("returns %Rrc\n", rc));
10628 return rc;
10629}
10630
10631/**
10632 * Get parent UUID of image in HDD container.
10633 *
10634 * @returns VBox status code.
10635 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
10636 * @param pDisk Pointer to HDD container.
10637 * @param nImage Image number, counts from 0. 0 is always base image of container.
10638 * @param pUuid Where to store the parent image UUID.
10639 */
10640VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
10641 PRTUUID pUuid)
10642{
10643 int rc = VINF_SUCCESS;
10644 int rc2;
10645 bool fLockRead = false;
10646
10647 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
10648 do
10649 {
10650 /* sanity check */
10651 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10652 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10653
10654 /* Check arguments. */
10655 AssertMsgBreakStmt(VALID_PTR(pUuid),
10656 ("pUuid=%#p\n", pUuid),
10657 rc = VERR_INVALID_PARAMETER);
10658
10659 rc2 = vdThreadStartRead(pDisk);
10660 AssertRC(rc2);
10661 fLockRead = true;
10662
10663 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
10664 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
10665
10666 rc = pImage->Backend->pfnGetParentUuid(pImage->pBackendData, pUuid);
10667 } while (0);
10668
10669 if (RT_UNLIKELY(fLockRead))
10670 {
10671 rc2 = vdThreadFinishRead(pDisk);
10672 AssertRC(rc2);
10673 }
10674
10675 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
10676 return rc;
10677}
10678
10679/**
10680 * Set the image's parent UUID. Should not be used by normal applications.
10681 *
10682 * @returns VBox status code.
10683 * @param pDisk Pointer to HDD container.
10684 * @param nImage Image number, counts from 0. 0 is always base image of container.
10685 * @param pUuid New parent UUID of the image. If NULL, a new UUID is created.
10686 */
10687VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
10688 PCRTUUID pUuid)
10689{
10690 int rc;
10691 int rc2;
10692 bool fLockWrite = false;
10693
10694 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
10695 pDisk, nImage, pUuid, pUuid));
10696 do
10697 {
10698 /* sanity check */
10699 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10700 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10701
10702 /* Check arguments. */
10703 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
10704 ("pUuid=%#p\n", pUuid),
10705 rc = VERR_INVALID_PARAMETER);
10706
10707 rc2 = vdThreadStartWrite(pDisk);
10708 AssertRC(rc2);
10709 fLockWrite = true;
10710
10711 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
10712 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
10713
10714 RTUUID Uuid;
10715 if (!pUuid)
10716 {
10717 RTUuidCreate(&Uuid);
10718 pUuid = &Uuid;
10719 }
10720 rc = pImage->Backend->pfnSetParentUuid(pImage->pBackendData, pUuid);
10721 } while (0);
10722
10723 if (RT_UNLIKELY(fLockWrite))
10724 {
10725 rc2 = vdThreadFinishWrite(pDisk);
10726 AssertRC(rc2);
10727 }
10728
10729 LogFlowFunc(("returns %Rrc\n", rc));
10730 return rc;
10731}
10732
10733
10734/**
10735 * Debug helper - dumps all opened images in HDD container into the log file.
10736 *
10737 * @param pDisk Pointer to HDD container.
10738 */
10739VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk)
10740{
10741 int rc2;
10742 bool fLockRead = false;
10743
10744 do
10745 {
10746 /* sanity check */
10747 AssertPtrBreak(pDisk);
10748 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10749
10750 if (!pDisk->pInterfaceError || !VALID_PTR(pDisk->pInterfaceError->pfnMessage))
10751 pDisk->pInterfaceError->pfnMessage = vdLogMessage;
10752
10753 rc2 = vdThreadStartRead(pDisk);
10754 AssertRC(rc2);
10755 fLockRead = true;
10756
10757 vdMessageWrapper(pDisk, "--- Dumping VD Disk, Images=%u\n", pDisk->cImages);
10758 for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
10759 {
10760 vdMessageWrapper(pDisk, "Dumping VD image \"%s\" (Backend=%s)\n",
10761 pImage->pszFilename, pImage->Backend->pszBackendName);
10762 pImage->Backend->pfnDump(pImage->pBackendData);
10763 }
10764 } while (0);
10765
10766 if (RT_UNLIKELY(fLockRead))
10767 {
10768 rc2 = vdThreadFinishRead(pDisk);
10769 AssertRC(rc2);
10770 }
10771}
10772
10773
10774VBOXDDU_DECL(int) VDDiscardRanges(PVBOXHDD pDisk, PCRTRANGE paRanges, unsigned cRanges)
10775{
10776 int rc;
10777 int rc2;
10778 bool fLockWrite = false;
10779
10780 LogFlowFunc(("pDisk=%#p paRanges=%#p cRanges=%u\n",
10781 pDisk, paRanges, cRanges));
10782 do
10783 {
10784 /* sanity check */
10785 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10786 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10787
10788 /* Check arguments. */
10789 AssertMsgBreakStmt(cRanges,
10790 ("cRanges=%u\n", cRanges),
10791 rc = VERR_INVALID_PARAMETER);
10792 AssertMsgBreakStmt(VALID_PTR(paRanges),
10793 ("paRanges=%#p\n", paRanges),
10794 rc = VERR_INVALID_PARAMETER);
10795
10796 rc2 = vdThreadStartWrite(pDisk);
10797 AssertRC(rc2);
10798 fLockWrite = true;
10799
10800 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
10801
10802 AssertMsgBreakStmt(pDisk->pLast->uOpenFlags & VD_OPEN_FLAGS_DISCARD,
10803 ("Discarding not supported\n"),
10804 rc = VERR_NOT_SUPPORTED);
10805
10806 VDIOCTX IoCtx;
10807 RTSEMEVENT hEventComplete = NIL_RTSEMEVENT;
10808
10809 rc = RTSemEventCreate(&hEventComplete);
10810 if (RT_FAILURE(rc))
10811 break;
10812
10813 vdIoCtxDiscardInit(&IoCtx, pDisk, paRanges, cRanges,
10814 vdIoCtxSyncComplete, pDisk, hEventComplete, NULL,
10815 vdDiscardHelperAsync, VDIOCTX_FLAGS_SYNC | VDIOCTX_FLAGS_DONT_FREE);
10816 rc = vdIoCtxProcessSync(&IoCtx, hEventComplete);
10817
10818 RTSemEventDestroy(hEventComplete);
10819 } while (0);
10820
10821 if (RT_UNLIKELY(fLockWrite))
10822 {
10823 rc2 = vdThreadFinishWrite(pDisk);
10824 AssertRC(rc2);
10825 }
10826
10827 LogFlowFunc(("returns %Rrc\n", rc));
10828 return rc;
10829}
10830
10831
10832VBOXDDU_DECL(int) VDAsyncRead(PVBOXHDD pDisk, uint64_t uOffset, size_t cbRead,
10833 PCRTSGBUF pcSgBuf,
10834 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
10835 void *pvUser1, void *pvUser2)
10836{
10837 int rc = VERR_VD_BLOCK_FREE;
10838 int rc2;
10839 bool fLockRead = false;
10840 PVDIOCTX pIoCtx = NULL;
10841
10842 LogFlowFunc(("pDisk=%#p uOffset=%llu pcSgBuf=%#p cbRead=%zu pvUser1=%#p pvUser2=%#p\n",
10843 pDisk, uOffset, pcSgBuf, cbRead, pvUser1, pvUser2));
10844
10845 do
10846 {
10847 /* sanity check */
10848 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10849 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10850
10851 /* Check arguments. */
10852 AssertMsgBreakStmt(cbRead,
10853 ("cbRead=%zu\n", cbRead),
10854 rc = VERR_INVALID_PARAMETER);
10855 AssertMsgBreakStmt(VALID_PTR(pcSgBuf),
10856 ("pcSgBuf=%#p\n", pcSgBuf),
10857 rc = VERR_INVALID_PARAMETER);
10858
10859 rc2 = vdThreadStartRead(pDisk);
10860 AssertRC(rc2);
10861 fLockRead = true;
10862
10863 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
10864 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
10865 uOffset, cbRead, pDisk->cbSize),
10866 rc = VERR_INVALID_PARAMETER);
10867 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
10868
10869 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_READ, uOffset,
10870 cbRead, pDisk->pLast, pcSgBuf,
10871 pfnComplete, pvUser1, pvUser2,
10872 NULL, vdReadHelperAsync,
10873 VDIOCTX_FLAGS_ZERO_FREE_BLOCKS);
10874 if (!pIoCtx)
10875 {
10876 rc = VERR_NO_MEMORY;
10877 break;
10878 }
10879
10880 rc = vdIoCtxProcessTryLockDefer(pIoCtx);
10881 if (rc == VINF_VD_ASYNC_IO_FINISHED)
10882 {
10883 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
10884 vdIoCtxFree(pDisk, pIoCtx);
10885 else
10886 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
10887 }
10888 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
10889 vdIoCtxFree(pDisk, pIoCtx);
10890
10891 } while (0);
10892
10893 if (RT_UNLIKELY(fLockRead) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
10894 {
10895 rc2 = vdThreadFinishRead(pDisk);
10896 AssertRC(rc2);
10897 }
10898
10899 LogFlowFunc(("returns %Rrc\n", rc));
10900 return rc;
10901}
10902
10903
10904VBOXDDU_DECL(int) VDAsyncWrite(PVBOXHDD pDisk, uint64_t uOffset, size_t cbWrite,
10905 PCRTSGBUF pcSgBuf,
10906 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
10907 void *pvUser1, void *pvUser2)
10908{
10909 int rc;
10910 int rc2;
10911 bool fLockWrite = false;
10912 PVDIOCTX pIoCtx = NULL;
10913
10914 LogFlowFunc(("pDisk=%#p uOffset=%llu cSgBuf=%#p cbWrite=%zu pvUser1=%#p pvUser2=%#p\n",
10915 pDisk, uOffset, pcSgBuf, cbWrite, pvUser1, pvUser2));
10916 do
10917 {
10918 /* sanity check */
10919 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10920 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10921
10922 /* Check arguments. */
10923 AssertMsgBreakStmt(cbWrite,
10924 ("cbWrite=%zu\n", cbWrite),
10925 rc = VERR_INVALID_PARAMETER);
10926 AssertMsgBreakStmt(VALID_PTR(pcSgBuf),
10927 ("pcSgBuf=%#p\n", pcSgBuf),
10928 rc = VERR_INVALID_PARAMETER);
10929
10930 rc2 = vdThreadStartWrite(pDisk);
10931 AssertRC(rc2);
10932 fLockWrite = true;
10933
10934 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
10935 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
10936 uOffset, cbWrite, pDisk->cbSize),
10937 rc = VERR_INVALID_PARAMETER);
10938 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
10939
10940 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_WRITE, uOffset,
10941 cbWrite, pDisk->pLast, pcSgBuf,
10942 pfnComplete, pvUser1, pvUser2,
10943 NULL, vdWriteHelperAsync,
10944 VDIOCTX_FLAGS_DEFAULT);
10945 if (!pIoCtx)
10946 {
10947 rc = VERR_NO_MEMORY;
10948 break;
10949 }
10950
10951 rc = vdIoCtxProcessTryLockDefer(pIoCtx);
10952 if (rc == VINF_VD_ASYNC_IO_FINISHED)
10953 {
10954 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
10955 vdIoCtxFree(pDisk, pIoCtx);
10956 else
10957 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
10958 }
10959 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
10960 vdIoCtxFree(pDisk, pIoCtx);
10961 } while (0);
10962
10963 if (RT_UNLIKELY(fLockWrite) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
10964 {
10965 rc2 = vdThreadFinishWrite(pDisk);
10966 AssertRC(rc2);
10967 }
10968
10969 LogFlowFunc(("returns %Rrc\n", rc));
10970 return rc;
10971}
10972
10973
10974VBOXDDU_DECL(int) VDAsyncFlush(PVBOXHDD pDisk, PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
10975 void *pvUser1, void *pvUser2)
10976{
10977 int rc;
10978 int rc2;
10979 bool fLockWrite = false;
10980 PVDIOCTX pIoCtx = NULL;
10981
10982 LogFlowFunc(("pDisk=%#p\n", pDisk));
10983
10984 do
10985 {
10986 /* sanity check */
10987 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10988 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10989
10990 rc2 = vdThreadStartWrite(pDisk);
10991 AssertRC(rc2);
10992 fLockWrite = true;
10993
10994 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
10995
10996 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_FLUSH, 0,
10997 0, pDisk->pLast, NULL,
10998 pfnComplete, pvUser1, pvUser2,
10999 NULL, vdFlushHelperAsync,
11000 VDIOCTX_FLAGS_DEFAULT);
11001 if (!pIoCtx)
11002 {
11003 rc = VERR_NO_MEMORY;
11004 break;
11005 }
11006
11007 rc = vdIoCtxProcessTryLockDefer(pIoCtx);
11008 if (rc == VINF_VD_ASYNC_IO_FINISHED)
11009 {
11010 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
11011 vdIoCtxFree(pDisk, pIoCtx);
11012 else
11013 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
11014 }
11015 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
11016 vdIoCtxFree(pDisk, pIoCtx);
11017 } while (0);
11018
11019 if (RT_UNLIKELY(fLockWrite) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
11020 {
11021 rc2 = vdThreadFinishWrite(pDisk);
11022 AssertRC(rc2);
11023 }
11024
11025 LogFlowFunc(("returns %Rrc\n", rc));
11026 return rc;
11027}
11028
11029VBOXDDU_DECL(int) VDAsyncDiscardRanges(PVBOXHDD pDisk, PCRTRANGE paRanges, unsigned cRanges,
11030 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
11031 void *pvUser1, void *pvUser2)
11032{
11033 int rc;
11034 int rc2;
11035 bool fLockWrite = false;
11036 PVDIOCTX pIoCtx = NULL;
11037
11038 LogFlowFunc(("pDisk=%#p\n", pDisk));
11039
11040 do
11041 {
11042 /* sanity check */
11043 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
11044 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
11045
11046 rc2 = vdThreadStartWrite(pDisk);
11047 AssertRC(rc2);
11048 fLockWrite = true;
11049
11050 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
11051
11052 pIoCtx = vdIoCtxDiscardAlloc(pDisk, paRanges, cRanges,
11053 pfnComplete, pvUser1, pvUser2, NULL,
11054 vdDiscardHelperAsync,
11055 VDIOCTX_FLAGS_DEFAULT);
11056 if (!pIoCtx)
11057 {
11058 rc = VERR_NO_MEMORY;
11059 break;
11060 }
11061
11062 rc = vdIoCtxProcessTryLockDefer(pIoCtx);
11063 if (rc == VINF_VD_ASYNC_IO_FINISHED)
11064 {
11065 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
11066 vdIoCtxFree(pDisk, pIoCtx);
11067 else
11068 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
11069 }
11070 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
11071 vdIoCtxFree(pDisk, pIoCtx);
11072 } while (0);
11073
11074 if (RT_UNLIKELY(fLockWrite) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
11075 {
11076 rc2 = vdThreadFinishWrite(pDisk);
11077 AssertRC(rc2);
11078 }
11079
11080 LogFlowFunc(("returns %Rrc\n", rc));
11081 return rc;
11082}
11083
11084VBOXDDU_DECL(int) VDRepair(PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
11085 const char *pszFilename, const char *pszBackend,
11086 uint32_t fFlags)
11087{
11088 int rc = VERR_NOT_SUPPORTED;
11089 PCVBOXHDDBACKEND pBackend = NULL;
11090 VDINTERFACEIOINT VDIfIoInt;
11091 VDINTERFACEIO VDIfIoFallback;
11092 PVDINTERFACEIO pInterfaceIo;
11093
11094 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
11095 /* Check arguments. */
11096 AssertMsgReturn(VALID_PTR(pszFilename) && *pszFilename,
11097 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
11098 VERR_INVALID_PARAMETER);
11099 AssertMsgReturn(VALID_PTR(pszBackend),
11100 ("pszBackend=%#p\n", pszBackend),
11101 VERR_INVALID_PARAMETER);
11102 AssertMsgReturn((fFlags & ~VD_REPAIR_FLAGS_MASK) == 0,
11103 ("fFlags=%#x\n", fFlags),
11104 VERR_INVALID_PARAMETER);
11105
11106 pInterfaceIo = VDIfIoGet(pVDIfsImage);
11107 if (!pInterfaceIo)
11108 {
11109 /*
11110 * Caller doesn't provide an I/O interface, create our own using the
11111 * native file API.
11112 */
11113 vdIfIoFallbackCallbacksSetup(&VDIfIoFallback);
11114 pInterfaceIo = &VDIfIoFallback;
11115 }
11116
11117 /* Set up the internal I/O interface. */
11118 AssertReturn(!VDIfIoIntGet(pVDIfsImage), VERR_INVALID_PARAMETER);
11119 VDIfIoInt.pfnOpen = vdIOIntOpenLimited;
11120 VDIfIoInt.pfnClose = vdIOIntCloseLimited;
11121 VDIfIoInt.pfnDelete = vdIOIntDeleteLimited;
11122 VDIfIoInt.pfnMove = vdIOIntMoveLimited;
11123 VDIfIoInt.pfnGetFreeSpace = vdIOIntGetFreeSpaceLimited;
11124 VDIfIoInt.pfnGetModificationTime = vdIOIntGetModificationTimeLimited;
11125 VDIfIoInt.pfnGetSize = vdIOIntGetSizeLimited;
11126 VDIfIoInt.pfnSetSize = vdIOIntSetSizeLimited;
11127 VDIfIoInt.pfnReadUser = vdIOIntReadUserLimited;
11128 VDIfIoInt.pfnWriteUser = vdIOIntWriteUserLimited;
11129 VDIfIoInt.pfnReadMeta = vdIOIntReadMetaLimited;
11130 VDIfIoInt.pfnWriteMeta = vdIOIntWriteMetaLimited;
11131 VDIfIoInt.pfnFlush = vdIOIntFlushLimited;
11132 rc = VDInterfaceAdd(&VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
11133 pInterfaceIo, sizeof(VDINTERFACEIOINT), &pVDIfsImage);
11134 AssertRC(rc);
11135
11136 rc = vdFindBackend(pszBackend, &pBackend);
11137 if (RT_SUCCESS(rc))
11138 {
11139 if (pBackend->pfnRepair)
11140 rc = pBackend->pfnRepair(pszFilename, pVDIfsDisk, pVDIfsImage, fFlags);
11141 else
11142 rc = VERR_VD_IMAGE_REPAIR_NOT_SUPPORTED;
11143 }
11144
11145 LogFlowFunc(("returns %Rrc\n", rc));
11146 return rc;
11147}
11148
11149
11150/*
11151 * generic plugin functions
11152 */
11153
11154/**
11155 * @interface_method_impl{VBOXHDDBACKEND,pfnComposeLocation}
11156 */
11157DECLCALLBACK(int) genericFileComposeLocation(PVDINTERFACE pConfig, char **pszLocation)
11158{
11159 RT_NOREF1(pConfig);
11160 *pszLocation = NULL;
11161 return VINF_SUCCESS;
11162}
11163
11164/**
11165 * @interface_method_impl{VBOXHDDBACKEND,pfnComposeName}
11166 */
11167DECLCALLBACK(int) genericFileComposeName(PVDINTERFACE pConfig, char **pszName)
11168{
11169 RT_NOREF1(pConfig);
11170 *pszName = NULL;
11171 return VINF_SUCCESS;
11172}
11173
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