VirtualBox

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

Last change on this file since 52585 was 52585, checked in by vboxsync, 10 years ago

Storage/VD + Main/ExtPackManager+VirtualBox+SystemProperties: handle unloading of VD plugin from VBoxSVC when extpack is uninstalled, fixes extpack uninstall problems related to VDPluginCrypt.dll on Windows

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