VirtualBox

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

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

Storage: Cleanup, let vdIfIoIntFileSetAllocationSize take a progress interface instead of just pfnProgress and pvUser

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette