VirtualBox

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

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

© 2023 Oracle
ContactPrivacy policyTerms of Use