VirtualBox

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

Last change on this file since 60813 was 60032, checked in by vboxsync, 9 years ago

Storage/VD: comment

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