VirtualBox

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

Last change on this file since 61600 was 60873, checked in by vboxsync, 9 years ago

please stay without 130 columns.

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