VirtualBox

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

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

Storage/VD: make use of the image type (hdd/dvd/floppy) for sanity checking when creating disk images

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