VirtualBox

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

Last change on this file since 58591 was 58132, checked in by vboxsync, 9 years ago

*: Doxygen fixes.

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