VirtualBox

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

Last change on this file since 57372 was 57358, checked in by vboxsync, 9 years ago

*: scm cleanup run.

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