VirtualBox

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

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

Storage/VD: Fix broken resume after the VM was suspended due to a full disk when the IDE controller is used

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