VirtualBox

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

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

Storage,DrvVD,Main,VBoxManage: Rename VBOXHDD to VDISK, the VBoxHDD module is long gone:

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