VirtualBox

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

Last change on this file since 55180 was 54835, checked in by vboxsync, 10 years ago

Encryption: Add a way to retrieve the encryption settings without providing the password. Returns only the used cipher and the password ID

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 388.4 KB
Line 
1/* $Id: VD.cpp 54835 2015-03-18 15:44:17Z vboxsync $ */
2/** @file
3 * VBoxHDD - VBox HDD Container implementation.
4 */
5
6/*
7 * Copyright (C) 2006-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*******************************************************************************
19* 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(("VD: 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, fFlags & VD_FILTER_FLAGS_INFO,
6781 pFilter->pVDIfsFilter, &pFilter->pvBackendData);
6782 if (RT_FAILURE(rc))
6783 break;
6784
6785 /* Lock disk for writing, as we modify pDisk information below. */
6786 rc2 = vdThreadStartWrite(pDisk);
6787 AssertRC(rc2);
6788 fLockWrite = true;
6789
6790 /* Add filter to chains. */
6791 if (fFlags & VD_FILTER_FLAGS_WRITE)
6792 {
6793 RTListAppend(&pDisk->ListFilterChainWrite, &pFilter->ListNodeChainWrite);
6794 vdFilterRetain(pFilter);
6795 }
6796
6797 if (fFlags & VD_FILTER_FLAGS_READ)
6798 {
6799 RTListAppend(&pDisk->ListFilterChainRead, &pFilter->ListNodeChainRead);
6800 vdFilterRetain(pFilter);
6801 }
6802 } while (0);
6803
6804 if (RT_UNLIKELY(fLockWrite))
6805 {
6806 rc2 = vdThreadFinishWrite(pDisk);
6807 AssertRC(rc2);
6808 }
6809
6810 if (RT_FAILURE(rc))
6811 {
6812 if (pFilter)
6813 RTMemFree(pFilter);
6814 }
6815
6816 LogFlowFunc(("returns %Rrc\n", rc));
6817 return rc;
6818}
6819
6820/**
6821 * Creates and opens a new base image file.
6822 *
6823 * @returns VBox status code.
6824 * @param pDisk Pointer to HDD container.
6825 * @param pszBackend Name of the image file backend to use.
6826 * @param pszFilename Name of the image file to create.
6827 * @param cbSize Image size in bytes.
6828 * @param uImageFlags Flags specifying special image features.
6829 * @param pszComment Pointer to image comment. NULL is ok.
6830 * @param pPCHSGeometry Pointer to physical disk geometry <= (16383,16,63). Not NULL.
6831 * @param pLCHSGeometry Pointer to logical disk geometry <= (x,255,63). Not NULL.
6832 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
6833 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
6834 * @param pVDIfsImage Pointer to the per-image VD interface list.
6835 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
6836 */
6837VBOXDDU_DECL(int) VDCreateBase(PVBOXHDD pDisk, const char *pszBackend,
6838 const char *pszFilename, uint64_t cbSize,
6839 unsigned uImageFlags, const char *pszComment,
6840 PCVDGEOMETRY pPCHSGeometry,
6841 PCVDGEOMETRY pLCHSGeometry,
6842 PCRTUUID pUuid, unsigned uOpenFlags,
6843 PVDINTERFACE pVDIfsImage,
6844 PVDINTERFACE pVDIfsOperation)
6845{
6846 int rc = VINF_SUCCESS;
6847 int rc2;
6848 bool fLockWrite = false, fLockRead = false;
6849 PVDIMAGE pImage = NULL;
6850 RTUUID uuid;
6851
6852 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",
6853 pDisk, pszBackend, pszFilename, cbSize, uImageFlags, pszComment,
6854 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
6855 pPCHSGeometry->cSectors, pLCHSGeometry->cCylinders,
6856 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors, pUuid,
6857 uOpenFlags, pVDIfsImage, pVDIfsOperation));
6858
6859 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
6860
6861 do
6862 {
6863 /* sanity check */
6864 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6865 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6866
6867 /* Check arguments. */
6868 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
6869 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
6870 rc = VERR_INVALID_PARAMETER);
6871 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
6872 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
6873 rc = VERR_INVALID_PARAMETER);
6874 AssertMsgBreakStmt(cbSize,
6875 ("cbSize=%llu\n", cbSize),
6876 rc = VERR_INVALID_PARAMETER);
6877 AssertMsgBreakStmt( ((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0)
6878 || ((uImageFlags & (VD_IMAGE_FLAGS_FIXED | VD_IMAGE_FLAGS_DIFF)) != VD_IMAGE_FLAGS_FIXED),
6879 ("uImageFlags=%#x\n", uImageFlags),
6880 rc = VERR_INVALID_PARAMETER);
6881 /* The PCHS geometry fields may be 0 to leave it for later. */
6882 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
6883 && pPCHSGeometry->cHeads <= 16
6884 && pPCHSGeometry->cSectors <= 63,
6885 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
6886 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
6887 pPCHSGeometry->cSectors),
6888 rc = VERR_INVALID_PARAMETER);
6889 /* The LCHS geometry fields may be 0 to leave it to later autodetection. */
6890 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
6891 && pLCHSGeometry->cHeads <= 255
6892 && pLCHSGeometry->cSectors <= 63,
6893 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
6894 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
6895 pLCHSGeometry->cSectors),
6896 rc = VERR_INVALID_PARAMETER);
6897 /* The UUID may be NULL. */
6898 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
6899 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
6900 rc = VERR_INVALID_PARAMETER);
6901 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
6902 ("uOpenFlags=%#x\n", uOpenFlags),
6903 rc = VERR_INVALID_PARAMETER);
6904
6905 /* Check state. Needs a temporary read lock. Holding the write lock
6906 * all the time would be blocking other activities for too long. */
6907 rc2 = vdThreadStartRead(pDisk);
6908 AssertRC(rc2);
6909 fLockRead = true;
6910 AssertMsgBreakStmt(pDisk->cImages == 0,
6911 ("Create base image cannot be done with other images open\n"),
6912 rc = VERR_VD_INVALID_STATE);
6913 rc2 = vdThreadFinishRead(pDisk);
6914 AssertRC(rc2);
6915 fLockRead = false;
6916
6917 /* Set up image descriptor. */
6918 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
6919 if (!pImage)
6920 {
6921 rc = VERR_NO_MEMORY;
6922 break;
6923 }
6924 pImage->pszFilename = RTStrDup(pszFilename);
6925 if (!pImage->pszFilename)
6926 {
6927 rc = VERR_NO_MEMORY;
6928 break;
6929 }
6930 pImage->VDIo.pDisk = pDisk;
6931 pImage->pVDIfsImage = pVDIfsImage;
6932
6933 /* Set up the I/O interface. */
6934 pImage->VDIo.pInterfaceIo = VDIfIoGet(pVDIfsImage);
6935 if (!pImage->VDIo.pInterfaceIo)
6936 {
6937 vdIfIoFallbackCallbacksSetup(&pImage->VDIo.VDIfIo);
6938 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIo.Core, "VD_IO", VDINTERFACETYPE_IO,
6939 pDisk, sizeof(VDINTERFACEIO), &pVDIfsImage);
6940 pImage->VDIo.pInterfaceIo = &pImage->VDIo.VDIfIo;
6941 }
6942
6943 /* Set up the internal I/O interface. */
6944 AssertBreakStmt(!VDIfIoIntGet(pVDIfsImage), rc = VERR_INVALID_PARAMETER);
6945 vdIfIoIntCallbacksSetup(&pImage->VDIo.VDIfIoInt);
6946 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
6947 &pImage->VDIo, sizeof(VDINTERFACEIOINT), &pImage->pVDIfsImage);
6948 AssertRC(rc);
6949
6950 rc = vdFindBackend(pszBackend, &pImage->Backend);
6951 if (RT_FAILURE(rc))
6952 break;
6953 if (!pImage->Backend)
6954 {
6955 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
6956 N_("VD: unknown backend name '%s'"), pszBackend);
6957 break;
6958 }
6959 if (!(pImage->Backend->uBackendCaps & ( VD_CAP_CREATE_FIXED
6960 | VD_CAP_CREATE_DYNAMIC)))
6961 {
6962 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
6963 N_("VD: backend '%s' cannot create base images"), pszBackend);
6964 break;
6965 }
6966
6967 /* Create UUID if the caller didn't specify one. */
6968 if (!pUuid)
6969 {
6970 rc = RTUuidCreate(&uuid);
6971 if (RT_FAILURE(rc))
6972 {
6973 rc = vdError(pDisk, rc, RT_SRC_POS,
6974 N_("VD: cannot generate UUID for image '%s'"),
6975 pszFilename);
6976 break;
6977 }
6978 pUuid = &uuid;
6979 }
6980
6981 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
6982 uImageFlags &= ~VD_IMAGE_FLAGS_DIFF;
6983 pImage->VDIo.fIgnoreFlush = (uOpenFlags & VD_OPEN_FLAGS_IGNORE_FLUSH) != 0;
6984 rc = pImage->Backend->pfnCreate(pImage->pszFilename, cbSize,
6985 uImageFlags, pszComment, pPCHSGeometry,
6986 pLCHSGeometry, pUuid,
6987 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
6988 0, 99,
6989 pDisk->pVDIfsDisk,
6990 pImage->pVDIfsImage,
6991 pVDIfsOperation,
6992 pDisk->enmType,
6993 &pImage->pBackendData);
6994
6995 if (RT_SUCCESS(rc))
6996 {
6997 pImage->VDIo.pBackendData = pImage->pBackendData;
6998 pImage->uImageFlags = uImageFlags;
6999
7000 /* Force sane optimization settings. It's not worth avoiding writes
7001 * to fixed size images. The overhead would have almost no payback. */
7002 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
7003 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
7004
7005 /* Lock disk for writing, as we modify pDisk information below. */
7006 rc2 = vdThreadStartWrite(pDisk);
7007 AssertRC(rc2);
7008 fLockWrite = true;
7009
7010 /** @todo optionally check UUIDs */
7011
7012 /* Re-check state, as the lock wasn't held and another image
7013 * creation call could have been done by another thread. */
7014 AssertMsgStmt(pDisk->cImages == 0,
7015 ("Create base image cannot be done with other images open\n"),
7016 rc = VERR_VD_INVALID_STATE);
7017 }
7018
7019 if (RT_SUCCESS(rc))
7020 {
7021 /* Cache disk information. */
7022 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
7023
7024 /* Cache PCHS geometry. */
7025 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
7026 &pDisk->PCHSGeometry);
7027 if (RT_FAILURE(rc2))
7028 {
7029 pDisk->PCHSGeometry.cCylinders = 0;
7030 pDisk->PCHSGeometry.cHeads = 0;
7031 pDisk->PCHSGeometry.cSectors = 0;
7032 }
7033 else
7034 {
7035 /* Make sure the CHS geometry is properly clipped. */
7036 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
7037 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
7038 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
7039 }
7040
7041 /* Cache LCHS geometry. */
7042 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
7043 &pDisk->LCHSGeometry);
7044 if (RT_FAILURE(rc2))
7045 {
7046 pDisk->LCHSGeometry.cCylinders = 0;
7047 pDisk->LCHSGeometry.cHeads = 0;
7048 pDisk->LCHSGeometry.cSectors = 0;
7049 }
7050 else
7051 {
7052 /* Make sure the CHS geometry is properly clipped. */
7053 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
7054 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
7055 }
7056
7057 /* Image successfully opened, make it the last image. */
7058 vdAddImageToList(pDisk, pImage);
7059 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
7060 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
7061 }
7062 else
7063 {
7064 /* Error detected, image may or may not be opened. Close and delete
7065 * image if it was opened. */
7066 if (pImage->pBackendData)
7067 {
7068 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, true);
7069 AssertRC(rc2);
7070 pImage->pBackendData = NULL;
7071 }
7072 }
7073 } while (0);
7074
7075 if (RT_UNLIKELY(fLockWrite))
7076 {
7077 rc2 = vdThreadFinishWrite(pDisk);
7078 AssertRC(rc2);
7079 }
7080 else if (RT_UNLIKELY(fLockRead))
7081 {
7082 rc2 = vdThreadFinishRead(pDisk);
7083 AssertRC(rc2);
7084 }
7085
7086 if (RT_FAILURE(rc))
7087 {
7088 if (pImage)
7089 {
7090 if (pImage->pszFilename)
7091 RTStrFree(pImage->pszFilename);
7092 RTMemFree(pImage);
7093 }
7094 }
7095
7096 if (RT_SUCCESS(rc) && pIfProgress && pIfProgress->pfnProgress)
7097 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
7098
7099 LogFlowFunc(("returns %Rrc\n", rc));
7100 return rc;
7101}
7102
7103/**
7104 * Creates and opens a new differencing image file in HDD container.
7105 * See comments for VDOpen function about differencing images.
7106 *
7107 * @returns VBox status code.
7108 * @param pDisk Pointer to HDD container.
7109 * @param pszBackend Name of the image file backend to use.
7110 * @param pszFilename Name of the differencing image file to create.
7111 * @param uImageFlags Flags specifying special image features.
7112 * @param pszComment Pointer to image comment. NULL is ok.
7113 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
7114 * @param pParentUuid New parent UUID of the image. If NULL, the UUID is queried automatically.
7115 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
7116 * @param pVDIfsImage Pointer to the per-image VD interface list.
7117 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
7118 */
7119VBOXDDU_DECL(int) VDCreateDiff(PVBOXHDD pDisk, const char *pszBackend,
7120 const char *pszFilename, unsigned uImageFlags,
7121 const char *pszComment, PCRTUUID pUuid,
7122 PCRTUUID pParentUuid, unsigned uOpenFlags,
7123 PVDINTERFACE pVDIfsImage,
7124 PVDINTERFACE pVDIfsOperation)
7125{
7126 int rc = VINF_SUCCESS;
7127 int rc2;
7128 bool fLockWrite = false, fLockRead = false;
7129 PVDIMAGE pImage = NULL;
7130 RTUUID uuid;
7131
7132 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
7133 pDisk, pszBackend, pszFilename, uImageFlags, pszComment, pUuid, uOpenFlags, pVDIfsImage, pVDIfsOperation));
7134
7135 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
7136
7137 do
7138 {
7139 /* sanity check */
7140 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7141 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7142
7143 /* Check arguments. */
7144 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
7145 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
7146 rc = VERR_INVALID_PARAMETER);
7147 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
7148 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
7149 rc = VERR_INVALID_PARAMETER);
7150 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
7151 ("uImageFlags=%#x\n", uImageFlags),
7152 rc = VERR_INVALID_PARAMETER);
7153 /* The UUID may be NULL. */
7154 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
7155 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
7156 rc = VERR_INVALID_PARAMETER);
7157 /* The parent UUID may be NULL. */
7158 AssertMsgBreakStmt(pParentUuid == NULL || VALID_PTR(pParentUuid),
7159 ("pParentUuid=%#p ParentUUID=%RTuuid\n", pParentUuid, pParentUuid),
7160 rc = VERR_INVALID_PARAMETER);
7161 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
7162 ("uOpenFlags=%#x\n", uOpenFlags),
7163 rc = VERR_INVALID_PARAMETER);
7164
7165 /* Check state. Needs a temporary read lock. Holding the write lock
7166 * all the time would be blocking other activities for too long. */
7167 rc2 = vdThreadStartRead(pDisk);
7168 AssertRC(rc2);
7169 fLockRead = true;
7170 AssertMsgBreakStmt(pDisk->cImages != 0,
7171 ("Create diff image cannot be done without other images open\n"),
7172 rc = VERR_VD_INVALID_STATE);
7173 rc2 = vdThreadFinishRead(pDisk);
7174 AssertRC(rc2);
7175 fLockRead = false;
7176
7177 /*
7178 * Destroy the current discard state first which might still have pending blocks
7179 * for the currently opened image which will be switched to readonly mode.
7180 */
7181 /* Lock disk for writing, as we modify pDisk information below. */
7182 rc2 = vdThreadStartWrite(pDisk);
7183 AssertRC(rc2);
7184 fLockWrite = true;
7185 rc = vdDiscardStateDestroy(pDisk);
7186 if (RT_FAILURE(rc))
7187 break;
7188 rc2 = vdThreadFinishWrite(pDisk);
7189 AssertRC(rc2);
7190 fLockWrite = false;
7191
7192 /* Set up image descriptor. */
7193 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
7194 if (!pImage)
7195 {
7196 rc = VERR_NO_MEMORY;
7197 break;
7198 }
7199 pImage->pszFilename = RTStrDup(pszFilename);
7200 if (!pImage->pszFilename)
7201 {
7202 rc = VERR_NO_MEMORY;
7203 break;
7204 }
7205
7206 rc = vdFindBackend(pszBackend, &pImage->Backend);
7207 if (RT_FAILURE(rc))
7208 break;
7209 if (!pImage->Backend)
7210 {
7211 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
7212 N_("VD: unknown backend name '%s'"), pszBackend);
7213 break;
7214 }
7215 if ( !(pImage->Backend->uBackendCaps & VD_CAP_DIFF)
7216 || !(pImage->Backend->uBackendCaps & ( VD_CAP_CREATE_FIXED
7217 | VD_CAP_CREATE_DYNAMIC)))
7218 {
7219 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
7220 N_("VD: backend '%s' cannot create diff images"), pszBackend);
7221 break;
7222 }
7223
7224 pImage->VDIo.pDisk = pDisk;
7225 pImage->pVDIfsImage = pVDIfsImage;
7226
7227 /* Set up the I/O interface. */
7228 pImage->VDIo.pInterfaceIo = VDIfIoGet(pVDIfsImage);
7229 if (!pImage->VDIo.pInterfaceIo)
7230 {
7231 vdIfIoFallbackCallbacksSetup(&pImage->VDIo.VDIfIo);
7232 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIo.Core, "VD_IO", VDINTERFACETYPE_IO,
7233 pDisk, sizeof(VDINTERFACEIO), &pVDIfsImage);
7234 pImage->VDIo.pInterfaceIo = &pImage->VDIo.VDIfIo;
7235 }
7236
7237 /* Set up the internal I/O interface. */
7238 AssertBreakStmt(!VDIfIoIntGet(pVDIfsImage), rc = VERR_INVALID_PARAMETER);
7239 vdIfIoIntCallbacksSetup(&pImage->VDIo.VDIfIoInt);
7240 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
7241 &pImage->VDIo, sizeof(VDINTERFACEIOINT), &pImage->pVDIfsImage);
7242 AssertRC(rc);
7243
7244 /* Create UUID if the caller didn't specify one. */
7245 if (!pUuid)
7246 {
7247 rc = RTUuidCreate(&uuid);
7248 if (RT_FAILURE(rc))
7249 {
7250 rc = vdError(pDisk, rc, RT_SRC_POS,
7251 N_("VD: cannot generate UUID for image '%s'"),
7252 pszFilename);
7253 break;
7254 }
7255 pUuid = &uuid;
7256 }
7257
7258 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
7259 pImage->VDIo.fIgnoreFlush = (uOpenFlags & VD_OPEN_FLAGS_IGNORE_FLUSH) != 0;
7260 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
7261 rc = pImage->Backend->pfnCreate(pImage->pszFilename, pDisk->cbSize,
7262 uImageFlags | VD_IMAGE_FLAGS_DIFF,
7263 pszComment, &pDisk->PCHSGeometry,
7264 &pDisk->LCHSGeometry, pUuid,
7265 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
7266 0, 99,
7267 pDisk->pVDIfsDisk,
7268 pImage->pVDIfsImage,
7269 pVDIfsOperation,
7270 pDisk->enmType,
7271 &pImage->pBackendData);
7272
7273 if (RT_SUCCESS(rc))
7274 {
7275 pImage->VDIo.pBackendData = pImage->pBackendData;
7276 pImage->uImageFlags = uImageFlags;
7277
7278 /* Lock disk for writing, as we modify pDisk information below. */
7279 rc2 = vdThreadStartWrite(pDisk);
7280 AssertRC(rc2);
7281 fLockWrite = true;
7282
7283 /* Switch previous image to read-only mode. */
7284 unsigned uOpenFlagsPrevImg;
7285 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
7286 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
7287 {
7288 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
7289 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pBackendData, uOpenFlagsPrevImg);
7290 }
7291
7292 /** @todo optionally check UUIDs */
7293
7294 /* Re-check state, as the lock wasn't held and another image
7295 * creation call could have been done by another thread. */
7296 AssertMsgStmt(pDisk->cImages != 0,
7297 ("Create diff image cannot be done without other images open\n"),
7298 rc = VERR_VD_INVALID_STATE);
7299 }
7300
7301 if (RT_SUCCESS(rc))
7302 {
7303 RTUUID Uuid;
7304 RTTIMESPEC ts;
7305
7306 if (pParentUuid && !RTUuidIsNull(pParentUuid))
7307 {
7308 Uuid = *pParentUuid;
7309 pImage->Backend->pfnSetParentUuid(pImage->pBackendData, &Uuid);
7310 }
7311 else
7312 {
7313 rc2 = pDisk->pLast->Backend->pfnGetUuid(pDisk->pLast->pBackendData,
7314 &Uuid);
7315 if (RT_SUCCESS(rc2))
7316 pImage->Backend->pfnSetParentUuid(pImage->pBackendData, &Uuid);
7317 }
7318 rc2 = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
7319 &Uuid);
7320 if (RT_SUCCESS(rc2))
7321 pImage->Backend->pfnSetParentModificationUuid(pImage->pBackendData,
7322 &Uuid);
7323 if (pDisk->pLast->Backend->pfnGetTimeStamp)
7324 rc2 = pDisk->pLast->Backend->pfnGetTimeStamp(pDisk->pLast->pBackendData,
7325 &ts);
7326 else
7327 rc2 = VERR_NOT_IMPLEMENTED;
7328 if (RT_SUCCESS(rc2) && pImage->Backend->pfnSetParentTimeStamp)
7329 pImage->Backend->pfnSetParentTimeStamp(pImage->pBackendData, &ts);
7330
7331 if (pImage->Backend->pfnSetParentFilename)
7332 rc2 = pImage->Backend->pfnSetParentFilename(pImage->pBackendData, pDisk->pLast->pszFilename);
7333 }
7334
7335 if (RT_SUCCESS(rc))
7336 {
7337 /* Image successfully opened, make it the last image. */
7338 vdAddImageToList(pDisk, pImage);
7339 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
7340 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
7341 }
7342 else
7343 {
7344 /* Error detected, but image opened. Close and delete image. */
7345 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, true);
7346 AssertRC(rc2);
7347 pImage->pBackendData = NULL;
7348 }
7349 } while (0);
7350
7351 if (RT_UNLIKELY(fLockWrite))
7352 {
7353 rc2 = vdThreadFinishWrite(pDisk);
7354 AssertRC(rc2);
7355 }
7356 else if (RT_UNLIKELY(fLockRead))
7357 {
7358 rc2 = vdThreadFinishRead(pDisk);
7359 AssertRC(rc2);
7360 }
7361
7362 if (RT_FAILURE(rc))
7363 {
7364 if (pImage)
7365 {
7366 if (pImage->pszFilename)
7367 RTStrFree(pImage->pszFilename);
7368 RTMemFree(pImage);
7369 }
7370 }
7371
7372 if (RT_SUCCESS(rc) && pIfProgress && pIfProgress->pfnProgress)
7373 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
7374
7375 LogFlowFunc(("returns %Rrc\n", rc));
7376 return rc;
7377}
7378
7379
7380/**
7381 * Creates and opens new cache image file in HDD container.
7382 *
7383 * @return VBox status code.
7384 * @param pDisk Name of the cache file backend to use (case insensitive).
7385 * @param pszFilename Name of the differencing cache file to create.
7386 * @param cbSize Maximum size of the cache.
7387 * @param uImageFlags Flags specifying special cache features.
7388 * @param pszComment Pointer to image comment. NULL is ok.
7389 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
7390 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
7391 * @param pVDIfsCache Pointer to the per-cache VD interface list.
7392 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
7393 */
7394VBOXDDU_DECL(int) VDCreateCache(PVBOXHDD pDisk, const char *pszBackend,
7395 const char *pszFilename, uint64_t cbSize,
7396 unsigned uImageFlags, const char *pszComment,
7397 PCRTUUID pUuid, unsigned uOpenFlags,
7398 PVDINTERFACE pVDIfsCache, PVDINTERFACE pVDIfsOperation)
7399{
7400 int rc = VINF_SUCCESS;
7401 int rc2;
7402 bool fLockWrite = false, fLockRead = false;
7403 PVDCACHE pCache = NULL;
7404 RTUUID uuid;
7405
7406 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
7407 pDisk, pszBackend, pszFilename, cbSize, uImageFlags, pszComment, pUuid, uOpenFlags, pVDIfsCache, pVDIfsOperation));
7408
7409 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
7410
7411 do
7412 {
7413 /* sanity check */
7414 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7415 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7416
7417 /* Check arguments. */
7418 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
7419 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
7420 rc = VERR_INVALID_PARAMETER);
7421 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
7422 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
7423 rc = VERR_INVALID_PARAMETER);
7424 AssertMsgBreakStmt(cbSize,
7425 ("cbSize=%llu\n", cbSize),
7426 rc = VERR_INVALID_PARAMETER);
7427 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
7428 ("uImageFlags=%#x\n", uImageFlags),
7429 rc = VERR_INVALID_PARAMETER);
7430 /* The UUID may be NULL. */
7431 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
7432 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
7433 rc = VERR_INVALID_PARAMETER);
7434 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
7435 ("uOpenFlags=%#x\n", uOpenFlags),
7436 rc = VERR_INVALID_PARAMETER);
7437
7438 /* Check state. Needs a temporary read lock. Holding the write lock
7439 * all the time would be blocking other activities for too long. */
7440 rc2 = vdThreadStartRead(pDisk);
7441 AssertRC(rc2);
7442 fLockRead = true;
7443 AssertMsgBreakStmt(!pDisk->pCache,
7444 ("Create cache image cannot be done with a cache already attached\n"),
7445 rc = VERR_VD_CACHE_ALREADY_EXISTS);
7446 rc2 = vdThreadFinishRead(pDisk);
7447 AssertRC(rc2);
7448 fLockRead = false;
7449
7450 /* Set up image descriptor. */
7451 pCache = (PVDCACHE)RTMemAllocZ(sizeof(VDCACHE));
7452 if (!pCache)
7453 {
7454 rc = VERR_NO_MEMORY;
7455 break;
7456 }
7457 pCache->pszFilename = RTStrDup(pszFilename);
7458 if (!pCache->pszFilename)
7459 {
7460 rc = VERR_NO_MEMORY;
7461 break;
7462 }
7463
7464 rc = vdFindCacheBackend(pszBackend, &pCache->Backend);
7465 if (RT_FAILURE(rc))
7466 break;
7467 if (!pCache->Backend)
7468 {
7469 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
7470 N_("VD: unknown backend name '%s'"), pszBackend);
7471 break;
7472 }
7473
7474 pCache->VDIo.pDisk = pDisk;
7475 pCache->pVDIfsCache = pVDIfsCache;
7476
7477 /* Set up the I/O interface. */
7478 pCache->VDIo.pInterfaceIo = VDIfIoGet(pVDIfsCache);
7479 if (!pCache->VDIo.pInterfaceIo)
7480 {
7481 vdIfIoFallbackCallbacksSetup(&pCache->VDIo.VDIfIo);
7482 rc = VDInterfaceAdd(&pCache->VDIo.VDIfIo.Core, "VD_IO", VDINTERFACETYPE_IO,
7483 pDisk, sizeof(VDINTERFACEIO), &pVDIfsCache);
7484 pCache->VDIo.pInterfaceIo = &pCache->VDIo.VDIfIo;
7485 }
7486
7487 /* Set up the internal I/O interface. */
7488 AssertBreakStmt(!VDIfIoIntGet(pVDIfsCache), rc = VERR_INVALID_PARAMETER);
7489 vdIfIoIntCallbacksSetup(&pCache->VDIo.VDIfIoInt);
7490 rc = VDInterfaceAdd(&pCache->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
7491 &pCache->VDIo, sizeof(VDINTERFACEIOINT), &pCache->pVDIfsCache);
7492 AssertRC(rc);
7493
7494 /* Create UUID if the caller didn't specify one. */
7495 if (!pUuid)
7496 {
7497 rc = RTUuidCreate(&uuid);
7498 if (RT_FAILURE(rc))
7499 {
7500 rc = vdError(pDisk, rc, RT_SRC_POS,
7501 N_("VD: cannot generate UUID for image '%s'"),
7502 pszFilename);
7503 break;
7504 }
7505 pUuid = &uuid;
7506 }
7507
7508 pCache->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
7509 pCache->VDIo.fIgnoreFlush = (uOpenFlags & VD_OPEN_FLAGS_IGNORE_FLUSH) != 0;
7510 rc = pCache->Backend->pfnCreate(pCache->pszFilename, cbSize,
7511 uImageFlags,
7512 pszComment, pUuid,
7513 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
7514 0, 99,
7515 pDisk->pVDIfsDisk,
7516 pCache->pVDIfsCache,
7517 pVDIfsOperation,
7518 &pCache->pBackendData);
7519
7520 if (RT_SUCCESS(rc))
7521 {
7522 /* Lock disk for writing, as we modify pDisk information below. */
7523 rc2 = vdThreadStartWrite(pDisk);
7524 AssertRC(rc2);
7525 fLockWrite = true;
7526
7527 pCache->VDIo.pBackendData = pCache->pBackendData;
7528
7529 /* Re-check state, as the lock wasn't held and another image
7530 * creation call could have been done by another thread. */
7531 AssertMsgStmt(!pDisk->pCache,
7532 ("Create cache image cannot be done with another cache open\n"),
7533 rc = VERR_VD_CACHE_ALREADY_EXISTS);
7534 }
7535
7536 if ( RT_SUCCESS(rc)
7537 && pDisk->pLast)
7538 {
7539 RTUUID UuidModification;
7540
7541 /* Set same modification Uuid as the last image. */
7542 rc = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
7543 &UuidModification);
7544 if (RT_SUCCESS(rc))
7545 {
7546 rc = pCache->Backend->pfnSetModificationUuid(pCache->pBackendData,
7547 &UuidModification);
7548 }
7549
7550 if (rc == VERR_NOT_SUPPORTED)
7551 rc = VINF_SUCCESS;
7552 }
7553
7554 if (RT_SUCCESS(rc))
7555 {
7556 /* Cache successfully created. */
7557 pDisk->pCache = pCache;
7558 }
7559 else
7560 {
7561 /* Error detected, but image opened. Close and delete image. */
7562 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, true);
7563 AssertRC(rc2);
7564 pCache->pBackendData = NULL;
7565 }
7566 } while (0);
7567
7568 if (RT_UNLIKELY(fLockWrite))
7569 {
7570 rc2 = vdThreadFinishWrite(pDisk);
7571 AssertRC(rc2);
7572 }
7573 else if (RT_UNLIKELY(fLockRead))
7574 {
7575 rc2 = vdThreadFinishRead(pDisk);
7576 AssertRC(rc2);
7577 }
7578
7579 if (RT_FAILURE(rc))
7580 {
7581 if (pCache)
7582 {
7583 if (pCache->pszFilename)
7584 RTStrFree(pCache->pszFilename);
7585 RTMemFree(pCache);
7586 }
7587 }
7588
7589 if (RT_SUCCESS(rc) && pIfProgress && pIfProgress->pfnProgress)
7590 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
7591
7592 LogFlowFunc(("returns %Rrc\n", rc));
7593 return rc;
7594}
7595
7596/**
7597 * Merges two images (not necessarily with direct parent/child relationship).
7598 * As a side effect the source image and potentially the other images which
7599 * are also merged to the destination are deleted from both the disk and the
7600 * images in the HDD container.
7601 *
7602 * @returns VBox status code.
7603 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7604 * @param pDisk Pointer to HDD container.
7605 * @param nImageFrom Name of the image file to merge from.
7606 * @param nImageTo Name of the image file to merge to.
7607 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
7608 */
7609VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
7610 unsigned nImageTo, PVDINTERFACE pVDIfsOperation)
7611{
7612 int rc = VINF_SUCCESS;
7613 int rc2;
7614 bool fLockWrite = false, fLockRead = false;
7615 void *pvBuf = NULL;
7616
7617 LogFlowFunc(("pDisk=%#p nImageFrom=%u nImageTo=%u pVDIfsOperation=%#p\n",
7618 pDisk, nImageFrom, nImageTo, pVDIfsOperation));
7619
7620 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
7621
7622 do
7623 {
7624 /* sanity check */
7625 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7626 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7627
7628 /* For simplicity reasons lock for writing as the image reopen below
7629 * might need it. After all the reopen is usually needed. */
7630 rc2 = vdThreadStartWrite(pDisk);
7631 AssertRC(rc2);
7632 fLockWrite = true;
7633 PVDIMAGE pImageFrom = vdGetImageByNumber(pDisk, nImageFrom);
7634 PVDIMAGE pImageTo = vdGetImageByNumber(pDisk, nImageTo);
7635 if (!pImageFrom || !pImageTo)
7636 {
7637 rc = VERR_VD_IMAGE_NOT_FOUND;
7638 break;
7639 }
7640 AssertBreakStmt(pImageFrom != pImageTo, rc = VERR_INVALID_PARAMETER);
7641
7642 /* Make sure destination image is writable. */
7643 unsigned uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pBackendData);
7644 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
7645 {
7646 /*
7647 * Clear skip consistency checks because the image is made writable now and
7648 * skipping consistency checks is only possible for readonly images.
7649 */
7650 uOpenFlags &= ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS);
7651 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
7652 uOpenFlags);
7653 if (RT_FAILURE(rc))
7654 break;
7655 }
7656
7657 /* Get size of destination image. */
7658 uint64_t cbSize = pImageTo->Backend->pfnGetSize(pImageTo->pBackendData);
7659 rc2 = vdThreadFinishWrite(pDisk);
7660 AssertRC(rc2);
7661 fLockWrite = false;
7662
7663 /* Allocate tmp buffer. */
7664 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
7665 if (!pvBuf)
7666 {
7667 rc = VERR_NO_MEMORY;
7668 break;
7669 }
7670
7671 /* Merging is done directly on the images itself. This potentially
7672 * causes trouble if the disk is full in the middle of operation. */
7673 if (nImageFrom < nImageTo)
7674 {
7675 /* Merge parent state into child. This means writing all not
7676 * allocated blocks in the destination image which are allocated in
7677 * the images to be merged. */
7678 uint64_t uOffset = 0;
7679 uint64_t cbRemaining = cbSize;
7680 do
7681 {
7682 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
7683 RTSGSEG SegmentBuf;
7684 RTSGBUF SgBuf;
7685 VDIOCTX IoCtx;
7686
7687 SegmentBuf.pvSeg = pvBuf;
7688 SegmentBuf.cbSeg = VD_MERGE_BUFFER_SIZE;
7689 RTSgBufInit(&SgBuf, &SegmentBuf, 1);
7690 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_READ, 0, 0, NULL,
7691 &SgBuf, NULL, NULL, VDIOCTX_FLAGS_SYNC);
7692
7693 /* Need to hold the write lock during a read-write operation. */
7694 rc2 = vdThreadStartWrite(pDisk);
7695 AssertRC(rc2);
7696 fLockWrite = true;
7697
7698 rc = pImageTo->Backend->pfnRead(pImageTo->pBackendData,
7699 uOffset, cbThisRead,
7700 &IoCtx, &cbThisRead);
7701 if (rc == VERR_VD_BLOCK_FREE)
7702 {
7703 /* Search for image with allocated block. Do not attempt to
7704 * read more than the previous reads marked as valid.
7705 * Otherwise this would return stale data when different
7706 * block sizes are used for the images. */
7707 for (PVDIMAGE pCurrImage = pImageTo->pPrev;
7708 pCurrImage != NULL && pCurrImage != pImageFrom->pPrev && rc == VERR_VD_BLOCK_FREE;
7709 pCurrImage = pCurrImage->pPrev)
7710 {
7711 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
7712 uOffset, cbThisRead,
7713 &IoCtx, &cbThisRead);
7714 }
7715
7716 if (rc != VERR_VD_BLOCK_FREE)
7717 {
7718 if (RT_FAILURE(rc))
7719 break;
7720 /* Updating the cache is required because this might be a live merge. */
7721 rc = vdWriteHelperEx(pDisk, pImageTo, pImageFrom->pPrev,
7722 uOffset, pvBuf, cbThisRead,
7723 VDIOCTX_FLAGS_READ_UPDATE_CACHE, 0);
7724 if (RT_FAILURE(rc))
7725 break;
7726 }
7727 else
7728 rc = VINF_SUCCESS;
7729 }
7730 else if (RT_FAILURE(rc))
7731 break;
7732
7733 rc2 = vdThreadFinishWrite(pDisk);
7734 AssertRC(rc2);
7735 fLockWrite = false;
7736
7737 uOffset += cbThisRead;
7738 cbRemaining -= cbThisRead;
7739
7740 if (pIfProgress && pIfProgress->pfnProgress)
7741 {
7742 /** @todo r=klaus: this can update the progress to the same
7743 * percentage over and over again if the image format makes
7744 * relatively small increments. */
7745 rc = pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
7746 uOffset * 99 / cbSize);
7747 if (RT_FAILURE(rc))
7748 break;
7749 }
7750 } while (uOffset < cbSize);
7751 }
7752 else
7753 {
7754 /*
7755 * We may need to update the parent uuid of the child coming after
7756 * the last image to be merged. We have to reopen it read/write.
7757 *
7758 * This is done before we do the actual merge to prevent an
7759 * inconsistent chain if the mode change fails for some reason.
7760 */
7761 if (pImageFrom->pNext)
7762 {
7763 PVDIMAGE pImageChild = pImageFrom->pNext;
7764
7765 /* Take the write lock. */
7766 rc2 = vdThreadStartWrite(pDisk);
7767 AssertRC(rc2);
7768 fLockWrite = true;
7769
7770 /* We need to open the image in read/write mode. */
7771 uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pBackendData);
7772
7773 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
7774 {
7775 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
7776 rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pBackendData,
7777 uOpenFlags);
7778 if (RT_FAILURE(rc))
7779 break;
7780 }
7781
7782 rc2 = vdThreadFinishWrite(pDisk);
7783 AssertRC(rc2);
7784 fLockWrite = false;
7785 }
7786
7787 /* If the merge is from the last image we have to relay all writes
7788 * to the merge destination as well, so that concurrent writes
7789 * (in case of a live merge) are handled correctly. */
7790 if (!pImageFrom->pNext)
7791 {
7792 /* Take the write lock. */
7793 rc2 = vdThreadStartWrite(pDisk);
7794 AssertRC(rc2);
7795 fLockWrite = true;
7796
7797 pDisk->pImageRelay = pImageTo;
7798
7799 rc2 = vdThreadFinishWrite(pDisk);
7800 AssertRC(rc2);
7801 fLockWrite = false;
7802 }
7803
7804 /* Merge child state into parent. This means writing all blocks
7805 * which are allocated in the image up to the source image to the
7806 * destination image. */
7807 uint64_t uOffset = 0;
7808 uint64_t cbRemaining = cbSize;
7809 do
7810 {
7811 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
7812 RTSGSEG SegmentBuf;
7813 RTSGBUF SgBuf;
7814 VDIOCTX IoCtx;
7815
7816 rc = VERR_VD_BLOCK_FREE;
7817
7818 SegmentBuf.pvSeg = pvBuf;
7819 SegmentBuf.cbSeg = VD_MERGE_BUFFER_SIZE;
7820 RTSgBufInit(&SgBuf, &SegmentBuf, 1);
7821 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_READ, 0, 0, NULL,
7822 &SgBuf, NULL, NULL, VDIOCTX_FLAGS_SYNC);
7823
7824 /* Need to hold the write lock during a read-write operation. */
7825 rc2 = vdThreadStartWrite(pDisk);
7826 AssertRC(rc2);
7827 fLockWrite = true;
7828
7829 /* Search for image with allocated block. Do not attempt to
7830 * read more than the previous reads marked as valid. Otherwise
7831 * this would return stale data when different block sizes are
7832 * used for the images. */
7833 for (PVDIMAGE pCurrImage = pImageFrom;
7834 pCurrImage != NULL && pCurrImage != pImageTo && rc == VERR_VD_BLOCK_FREE;
7835 pCurrImage = pCurrImage->pPrev)
7836 {
7837 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
7838 uOffset, cbThisRead,
7839 &IoCtx, &cbThisRead);
7840 }
7841
7842 if (rc != VERR_VD_BLOCK_FREE)
7843 {
7844 if (RT_FAILURE(rc))
7845 break;
7846 rc = vdWriteHelper(pDisk, pImageTo, uOffset, pvBuf,
7847 cbThisRead, VDIOCTX_FLAGS_READ_UPDATE_CACHE);
7848 if (RT_FAILURE(rc))
7849 break;
7850 }
7851 else
7852 rc = VINF_SUCCESS;
7853
7854 rc2 = vdThreadFinishWrite(pDisk);
7855 AssertRC(rc2);
7856 fLockWrite = false;
7857
7858 uOffset += cbThisRead;
7859 cbRemaining -= cbThisRead;
7860
7861 if (pIfProgress && pIfProgress->pfnProgress)
7862 {
7863 /** @todo r=klaus: this can update the progress to the same
7864 * percentage over and over again if the image format makes
7865 * relatively small increments. */
7866 rc = pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
7867 uOffset * 99 / cbSize);
7868 if (RT_FAILURE(rc))
7869 break;
7870 }
7871 } while (uOffset < cbSize);
7872
7873 /* In case we set up a "write proxy" image above we must clear
7874 * this again now to prevent stray writes. Failure or not. */
7875 if (!pImageFrom->pNext)
7876 {
7877 /* Take the write lock. */
7878 rc2 = vdThreadStartWrite(pDisk);
7879 AssertRC(rc2);
7880 fLockWrite = true;
7881
7882 pDisk->pImageRelay = NULL;
7883
7884 rc2 = vdThreadFinishWrite(pDisk);
7885 AssertRC(rc2);
7886 fLockWrite = false;
7887 }
7888 }
7889
7890 /*
7891 * Leave in case of an error to avoid corrupted data in the image chain
7892 * (includes cancelling the operation by the user).
7893 */
7894 if (RT_FAILURE(rc))
7895 break;
7896
7897 /* Need to hold the write lock while finishing the merge. */
7898 rc2 = vdThreadStartWrite(pDisk);
7899 AssertRC(rc2);
7900 fLockWrite = true;
7901
7902 /* Update parent UUID so that image chain is consistent.
7903 * The two attempts work around the problem that some backends
7904 * (e.g. iSCSI) do not support UUIDs, so we exploit the fact that
7905 * so far there can only be one such image in the chain. */
7906 /** @todo needs a better long-term solution, passing the UUID
7907 * knowledge from the caller or some such */
7908 RTUUID Uuid;
7909 PVDIMAGE pImageChild = NULL;
7910 if (nImageFrom < nImageTo)
7911 {
7912 if (pImageFrom->pPrev)
7913 {
7914 /* plan A: ask the parent itself for its UUID */
7915 rc = pImageFrom->pPrev->Backend->pfnGetUuid(pImageFrom->pPrev->pBackendData,
7916 &Uuid);
7917 if (RT_FAILURE(rc))
7918 {
7919 /* plan B: ask the child of the parent for parent UUID */
7920 rc = pImageFrom->Backend->pfnGetParentUuid(pImageFrom->pBackendData,
7921 &Uuid);
7922 }
7923 AssertRC(rc);
7924 }
7925 else
7926 RTUuidClear(&Uuid);
7927 rc = pImageTo->Backend->pfnSetParentUuid(pImageTo->pBackendData,
7928 &Uuid);
7929 AssertRC(rc);
7930 }
7931 else
7932 {
7933 /* Update the parent uuid of the child of the last merged image. */
7934 if (pImageFrom->pNext)
7935 {
7936 /* plan A: ask the parent itself for its UUID */
7937 rc = pImageTo->Backend->pfnGetUuid(pImageTo->pBackendData,
7938 &Uuid);
7939 if (RT_FAILURE(rc))
7940 {
7941 /* plan B: ask the child of the parent for parent UUID */
7942 rc = pImageTo->pNext->Backend->pfnGetParentUuid(pImageTo->pNext->pBackendData,
7943 &Uuid);
7944 }
7945 AssertRC(rc);
7946
7947 rc = pImageFrom->Backend->pfnSetParentUuid(pImageFrom->pNext->pBackendData,
7948 &Uuid);
7949 AssertRC(rc);
7950
7951 pImageChild = pImageFrom->pNext;
7952 }
7953 }
7954
7955 /* Delete the no longer needed images. */
7956 PVDIMAGE pImg = pImageFrom, pTmp;
7957 while (pImg != pImageTo)
7958 {
7959 if (nImageFrom < nImageTo)
7960 pTmp = pImg->pNext;
7961 else
7962 pTmp = pImg->pPrev;
7963 vdRemoveImageFromList(pDisk, pImg);
7964 pImg->Backend->pfnClose(pImg->pBackendData, true);
7965 RTMemFree(pImg->pszFilename);
7966 RTMemFree(pImg);
7967 pImg = pTmp;
7968 }
7969
7970 /* Make sure destination image is back to read only if necessary. */
7971 if (pImageTo != pDisk->pLast)
7972 {
7973 uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pBackendData);
7974 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
7975 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
7976 uOpenFlags);
7977 if (RT_FAILURE(rc))
7978 break;
7979 }
7980
7981 /*
7982 * Make sure the child is readonly
7983 * for the child -> parent merge direction
7984 * if necessary.
7985 */
7986 if ( nImageFrom > nImageTo
7987 && pImageChild
7988 && pImageChild != pDisk->pLast)
7989 {
7990 uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pBackendData);
7991 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
7992 rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pBackendData,
7993 uOpenFlags);
7994 if (RT_FAILURE(rc))
7995 break;
7996 }
7997 } while (0);
7998
7999 if (RT_UNLIKELY(fLockWrite))
8000 {
8001 rc2 = vdThreadFinishWrite(pDisk);
8002 AssertRC(rc2);
8003 }
8004 else if (RT_UNLIKELY(fLockRead))
8005 {
8006 rc2 = vdThreadFinishRead(pDisk);
8007 AssertRC(rc2);
8008 }
8009
8010 if (pvBuf)
8011 RTMemTmpFree(pvBuf);
8012
8013 if (RT_SUCCESS(rc) && pIfProgress && pIfProgress->pfnProgress)
8014 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
8015
8016 LogFlowFunc(("returns %Rrc\n", rc));
8017 return rc;
8018}
8019
8020/**
8021 * Copies an image from one HDD container to another - extended version.
8022 * The copy is opened in the target HDD container.
8023 * It is possible to convert between different image formats, because the
8024 * backend for the destination may be different from the source.
8025 * If both the source and destination reference the same HDD container,
8026 * then the image is moved (by copying/deleting or renaming) to the new location.
8027 * The source container is unchanged if the move operation fails, otherwise
8028 * the image at the new location is opened in the same way as the old one was.
8029 *
8030 * @note The read/write accesses across disks are not synchronized, just the
8031 * accesses to each disk. Once there is a use case which requires a defined
8032 * read/write behavior in this situation this needs to be extended.
8033 *
8034 * @return VBox status code.
8035 * @return VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
8036 * @param pDiskFrom Pointer to source HDD container.
8037 * @param nImage Image number, counts from 0. 0 is always base image of container.
8038 * @param pDiskTo Pointer to destination HDD container.
8039 * @param pszBackend Name of the image file backend to use (may be NULL to use the same as the source, case insensitive).
8040 * @param pszFilename New name of the image (may be NULL to specify that the
8041 * copy destination is the destination container, or
8042 * if pDiskFrom == pDiskTo, i.e. when moving).
8043 * @param fMoveByRename If true, attempt to perform a move by renaming (if successful the new size is ignored).
8044 * @param cbSize New image size (0 means leave unchanged).
8045 * @param nImageSameFrom todo
8046 * @param nImageSameTo todo
8047 * @param uImageFlags Flags specifying special destination image features.
8048 * @param pDstUuid New UUID of the destination image. If NULL, a new UUID is created.
8049 * This parameter is used if and only if a true copy is created.
8050 * In all rename/move cases or copy to existing image cases the modification UUIDs are copied over.
8051 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
8052 * Only used if the destination image is created.
8053 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
8054 * @param pDstVDIfsImage Pointer to the per-image VD interface list, for the
8055 * destination image.
8056 * @param pDstVDIfsOperation Pointer to the per-operation VD interface list,
8057 * for the destination operation.
8058 */
8059VBOXDDU_DECL(int) VDCopyEx(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
8060 const char *pszBackend, const char *pszFilename,
8061 bool fMoveByRename, uint64_t cbSize,
8062 unsigned nImageFromSame, unsigned nImageToSame,
8063 unsigned uImageFlags, PCRTUUID pDstUuid,
8064 unsigned uOpenFlags, PVDINTERFACE pVDIfsOperation,
8065 PVDINTERFACE pDstVDIfsImage,
8066 PVDINTERFACE pDstVDIfsOperation)
8067{
8068 int rc = VINF_SUCCESS;
8069 int rc2;
8070 bool fLockReadFrom = false, fLockWriteFrom = false, fLockWriteTo = false;
8071 PVDIMAGE pImageTo = NULL;
8072
8073 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",
8074 pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename, cbSize, nImageFromSame, nImageToSame, uImageFlags, pDstUuid, uOpenFlags, pVDIfsOperation, pDstVDIfsImage, pDstVDIfsOperation));
8075
8076 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
8077 PVDINTERFACEPROGRESS pDstIfProgress = VDIfProgressGet(pDstVDIfsOperation);
8078
8079 do {
8080 /* Check arguments. */
8081 AssertMsgBreakStmt(VALID_PTR(pDiskFrom), ("pDiskFrom=%#p\n", pDiskFrom),
8082 rc = VERR_INVALID_PARAMETER);
8083 AssertMsg(pDiskFrom->u32Signature == VBOXHDDDISK_SIGNATURE,
8084 ("u32Signature=%08x\n", pDiskFrom->u32Signature));
8085
8086 rc2 = vdThreadStartRead(pDiskFrom);
8087 AssertRC(rc2);
8088 fLockReadFrom = true;
8089 PVDIMAGE pImageFrom = vdGetImageByNumber(pDiskFrom, nImage);
8090 AssertPtrBreakStmt(pImageFrom, rc = VERR_VD_IMAGE_NOT_FOUND);
8091 AssertMsgBreakStmt(VALID_PTR(pDiskTo), ("pDiskTo=%#p\n", pDiskTo),
8092 rc = VERR_INVALID_PARAMETER);
8093 AssertMsg(pDiskTo->u32Signature == VBOXHDDDISK_SIGNATURE,
8094 ("u32Signature=%08x\n", pDiskTo->u32Signature));
8095 AssertMsgBreakStmt( (nImageFromSame < nImage || nImageFromSame == VD_IMAGE_CONTENT_UNKNOWN)
8096 && (nImageToSame < pDiskTo->cImages || nImageToSame == VD_IMAGE_CONTENT_UNKNOWN)
8097 && ( (nImageFromSame == VD_IMAGE_CONTENT_UNKNOWN && nImageToSame == VD_IMAGE_CONTENT_UNKNOWN)
8098 || (nImageFromSame != VD_IMAGE_CONTENT_UNKNOWN && nImageToSame != VD_IMAGE_CONTENT_UNKNOWN)),
8099 ("nImageFromSame=%u nImageToSame=%u\n", nImageFromSame, nImageToSame),
8100 rc = VERR_INVALID_PARAMETER);
8101
8102 /* Move the image. */
8103 if (pDiskFrom == pDiskTo)
8104 {
8105 /* Rename only works when backends are the same, are file based
8106 * and the rename method is implemented. */
8107 if ( fMoveByRename
8108 && !RTStrICmp(pszBackend, pImageFrom->Backend->pszBackendName)
8109 && pImageFrom->Backend->uBackendCaps & VD_CAP_FILE
8110 && pImageFrom->Backend->pfnRename)
8111 {
8112 rc2 = vdThreadFinishRead(pDiskFrom);
8113 AssertRC(rc2);
8114 fLockReadFrom = false;
8115
8116 rc2 = vdThreadStartWrite(pDiskFrom);
8117 AssertRC(rc2);
8118 fLockWriteFrom = true;
8119 rc = pImageFrom->Backend->pfnRename(pImageFrom->pBackendData, pszFilename ? pszFilename : pImageFrom->pszFilename);
8120 break;
8121 }
8122
8123 /** @todo Moving (including shrinking/growing) of the image is
8124 * requested, but the rename attempt failed or it wasn't possible.
8125 * Must now copy image to temp location. */
8126 AssertReleaseMsgFailed(("VDCopy: moving by copy/delete not implemented\n"));
8127 }
8128
8129 /* pszFilename is allowed to be NULL, as this indicates copy to the existing image. */
8130 AssertMsgBreakStmt(pszFilename == NULL || (VALID_PTR(pszFilename) && *pszFilename),
8131 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
8132 rc = VERR_INVALID_PARAMETER);
8133
8134 uint64_t cbSizeFrom;
8135 cbSizeFrom = pImageFrom->Backend->pfnGetSize(pImageFrom->pBackendData);
8136 if (cbSizeFrom == 0)
8137 {
8138 rc = VERR_VD_VALUE_NOT_FOUND;
8139 break;
8140 }
8141
8142 VDGEOMETRY PCHSGeometryFrom = {0, 0, 0};
8143 VDGEOMETRY LCHSGeometryFrom = {0, 0, 0};
8144 pImageFrom->Backend->pfnGetPCHSGeometry(pImageFrom->pBackendData, &PCHSGeometryFrom);
8145 pImageFrom->Backend->pfnGetLCHSGeometry(pImageFrom->pBackendData, &LCHSGeometryFrom);
8146
8147 RTUUID ImageUuid, ImageModificationUuid;
8148 if (pDiskFrom != pDiskTo)
8149 {
8150 if (pDstUuid)
8151 ImageUuid = *pDstUuid;
8152 else
8153 RTUuidCreate(&ImageUuid);
8154 }
8155 else
8156 {
8157 rc = pImageFrom->Backend->pfnGetUuid(pImageFrom->pBackendData, &ImageUuid);
8158 if (RT_FAILURE(rc))
8159 RTUuidCreate(&ImageUuid);
8160 }
8161 rc = pImageFrom->Backend->pfnGetModificationUuid(pImageFrom->pBackendData, &ImageModificationUuid);
8162 if (RT_FAILURE(rc))
8163 RTUuidClear(&ImageModificationUuid);
8164
8165 char szComment[1024];
8166 rc = pImageFrom->Backend->pfnGetComment(pImageFrom->pBackendData, szComment, sizeof(szComment));
8167 if (RT_FAILURE(rc))
8168 szComment[0] = '\0';
8169 else
8170 szComment[sizeof(szComment) - 1] = '\0';
8171
8172 rc2 = vdThreadFinishRead(pDiskFrom);
8173 AssertRC(rc2);
8174 fLockReadFrom = false;
8175
8176 rc2 = vdThreadStartRead(pDiskTo);
8177 AssertRC(rc2);
8178 unsigned cImagesTo = pDiskTo->cImages;
8179 rc2 = vdThreadFinishRead(pDiskTo);
8180 AssertRC(rc2);
8181
8182 if (pszFilename)
8183 {
8184 if (cbSize == 0)
8185 cbSize = cbSizeFrom;
8186
8187 /* Create destination image with the properties of source image. */
8188 /** @todo replace the VDCreateDiff/VDCreateBase calls by direct
8189 * calls to the backend. Unifies the code and reduces the API
8190 * dependencies. Would also make the synchronization explicit. */
8191 if (cImagesTo > 0)
8192 {
8193 rc = VDCreateDiff(pDiskTo, pszBackend, pszFilename,
8194 uImageFlags, szComment, &ImageUuid,
8195 NULL /* pParentUuid */,
8196 uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
8197 pDstVDIfsImage, NULL);
8198
8199 rc2 = vdThreadStartWrite(pDiskTo);
8200 AssertRC(rc2);
8201 fLockWriteTo = true;
8202 } else {
8203 /** @todo hack to force creation of a fixed image for
8204 * the RAW backend, which can't handle anything else. */
8205 if (!RTStrICmp(pszBackend, "RAW"))
8206 uImageFlags |= VD_IMAGE_FLAGS_FIXED;
8207
8208 vdFixupPCHSGeometry(&PCHSGeometryFrom, cbSize);
8209 vdFixupLCHSGeometry(&LCHSGeometryFrom, cbSize);
8210
8211 rc = VDCreateBase(pDiskTo, pszBackend, pszFilename, cbSize,
8212 uImageFlags, szComment,
8213 &PCHSGeometryFrom, &LCHSGeometryFrom,
8214 NULL, uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
8215 pDstVDIfsImage, NULL);
8216
8217 rc2 = vdThreadStartWrite(pDiskTo);
8218 AssertRC(rc2);
8219 fLockWriteTo = true;
8220
8221 if (RT_SUCCESS(rc) && !RTUuidIsNull(&ImageUuid))
8222 pDiskTo->pLast->Backend->pfnSetUuid(pDiskTo->pLast->pBackendData, &ImageUuid);
8223 }
8224 if (RT_FAILURE(rc))
8225 break;
8226
8227 pImageTo = pDiskTo->pLast;
8228 AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
8229
8230 cbSize = RT_MIN(cbSize, cbSizeFrom);
8231 }
8232 else
8233 {
8234 pImageTo = pDiskTo->pLast;
8235 AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
8236
8237 uint64_t cbSizeTo;
8238 cbSizeTo = pImageTo->Backend->pfnGetSize(pImageTo->pBackendData);
8239 if (cbSizeTo == 0)
8240 {
8241 rc = VERR_VD_VALUE_NOT_FOUND;
8242 break;
8243 }
8244
8245 if (cbSize == 0)
8246 cbSize = RT_MIN(cbSizeFrom, cbSizeTo);
8247
8248 vdFixupPCHSGeometry(&PCHSGeometryFrom, cbSize);
8249 vdFixupLCHSGeometry(&LCHSGeometryFrom, cbSize);
8250
8251 /* Update the geometry in the destination image. */
8252 pImageTo->Backend->pfnSetPCHSGeometry(pImageTo->pBackendData, &PCHSGeometryFrom);
8253 pImageTo->Backend->pfnSetLCHSGeometry(pImageTo->pBackendData, &LCHSGeometryFrom);
8254 }
8255
8256 rc2 = vdThreadFinishWrite(pDiskTo);
8257 AssertRC(rc2);
8258 fLockWriteTo = false;
8259
8260 /* Whether we can take the optimized copy path (false) or not.
8261 * Don't optimize if the image existed or if it is a child image. */
8262 bool fSuppressRedundantIo = ( !(pszFilename == NULL || cImagesTo > 0)
8263 || (nImageToSame != VD_IMAGE_CONTENT_UNKNOWN));
8264 unsigned cImagesFromReadBack, cImagesToReadBack;
8265
8266 if (nImageFromSame == VD_IMAGE_CONTENT_UNKNOWN)
8267 cImagesFromReadBack = 0;
8268 else
8269 {
8270 if (nImage == VD_LAST_IMAGE)
8271 cImagesFromReadBack = pDiskFrom->cImages - nImageFromSame - 1;
8272 else
8273 cImagesFromReadBack = nImage - nImageFromSame;
8274 }
8275
8276 if (nImageToSame == VD_IMAGE_CONTENT_UNKNOWN)
8277 cImagesToReadBack = 0;
8278 else
8279 cImagesToReadBack = pDiskTo->cImages - nImageToSame - 1;
8280
8281 /* Copy the data. */
8282 rc = vdCopyHelper(pDiskFrom, pImageFrom, pDiskTo, cbSize,
8283 cImagesFromReadBack, cImagesToReadBack,
8284 fSuppressRedundantIo, pIfProgress, pDstIfProgress);
8285
8286 if (RT_SUCCESS(rc))
8287 {
8288 rc2 = vdThreadStartWrite(pDiskTo);
8289 AssertRC(rc2);
8290 fLockWriteTo = true;
8291
8292 /* Only set modification UUID if it is non-null, since the source
8293 * backend might not provide a valid modification UUID. */
8294 if (!RTUuidIsNull(&ImageModificationUuid))
8295 pImageTo->Backend->pfnSetModificationUuid(pImageTo->pBackendData, &ImageModificationUuid);
8296
8297 /* Set the requested open flags if they differ from the value
8298 * required for creating the image and copying the contents. */
8299 if ( pImageTo && pszFilename
8300 && uOpenFlags != (uOpenFlags & ~VD_OPEN_FLAGS_READONLY))
8301 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
8302 uOpenFlags);
8303 }
8304 } while (0);
8305
8306 if (RT_FAILURE(rc) && pImageTo && pszFilename)
8307 {
8308 /* Take the write lock only if it is not taken. Not worth making the
8309 * above code even more complicated. */
8310 if (RT_UNLIKELY(!fLockWriteTo))
8311 {
8312 rc2 = vdThreadStartWrite(pDiskTo);
8313 AssertRC(rc2);
8314 fLockWriteTo = true;
8315 }
8316 /* Error detected, but new image created. Remove image from list. */
8317 vdRemoveImageFromList(pDiskTo, pImageTo);
8318
8319 /* Close and delete image. */
8320 rc2 = pImageTo->Backend->pfnClose(pImageTo->pBackendData, true);
8321 AssertRC(rc2);
8322 pImageTo->pBackendData = NULL;
8323
8324 /* Free remaining resources. */
8325 if (pImageTo->pszFilename)
8326 RTStrFree(pImageTo->pszFilename);
8327
8328 RTMemFree(pImageTo);
8329 }
8330
8331 if (RT_UNLIKELY(fLockWriteTo))
8332 {
8333 rc2 = vdThreadFinishWrite(pDiskTo);
8334 AssertRC(rc2);
8335 }
8336 if (RT_UNLIKELY(fLockWriteFrom))
8337 {
8338 rc2 = vdThreadFinishWrite(pDiskFrom);
8339 AssertRC(rc2);
8340 }
8341 else if (RT_UNLIKELY(fLockReadFrom))
8342 {
8343 rc2 = vdThreadFinishRead(pDiskFrom);
8344 AssertRC(rc2);
8345 }
8346
8347 if (RT_SUCCESS(rc))
8348 {
8349 if (pIfProgress && pIfProgress->pfnProgress)
8350 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
8351 if (pDstIfProgress && pDstIfProgress->pfnProgress)
8352 pDstIfProgress->pfnProgress(pDstIfProgress->Core.pvUser, 100);
8353 }
8354
8355 LogFlowFunc(("returns %Rrc\n", rc));
8356 return rc;
8357}
8358
8359/**
8360 * Copies an image from one HDD container to another.
8361 * The copy is opened in the target HDD container.
8362 * It is possible to convert between different image formats, because the
8363 * backend for the destination may be different from the source.
8364 * If both the source and destination reference the same HDD container,
8365 * then the image is moved (by copying/deleting or renaming) to the new location.
8366 * The source container is unchanged if the move operation fails, otherwise
8367 * the image at the new location is opened in the same way as the old one was.
8368 *
8369 * @returns VBox status code.
8370 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
8371 * @param pDiskFrom Pointer to source HDD container.
8372 * @param nImage Image number, counts from 0. 0 is always base image of container.
8373 * @param pDiskTo Pointer to destination HDD container.
8374 * @param pszBackend Name of the image file backend to use.
8375 * @param pszFilename New name of the image (may be NULL if pDiskFrom == pDiskTo).
8376 * @param fMoveByRename If true, attempt to perform a move by renaming (if successful the new size is ignored).
8377 * @param cbSize New image size (0 means leave unchanged).
8378 * @param uImageFlags Flags specifying special destination image features.
8379 * @param pDstUuid New UUID of the destination image. If NULL, a new UUID is created.
8380 * This parameter is used if and only if a true copy is created.
8381 * In all rename/move cases the UUIDs are copied over.
8382 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
8383 * Only used if the destination image is created.
8384 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
8385 * @param pDstVDIfsImage Pointer to the per-image VD interface list, for the
8386 * destination image.
8387 * @param pDstVDIfsOperation Pointer to the per-image VD interface list,
8388 * for the destination image.
8389 */
8390VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
8391 const char *pszBackend, const char *pszFilename,
8392 bool fMoveByRename, uint64_t cbSize,
8393 unsigned uImageFlags, PCRTUUID pDstUuid,
8394 unsigned uOpenFlags, PVDINTERFACE pVDIfsOperation,
8395 PVDINTERFACE pDstVDIfsImage,
8396 PVDINTERFACE pDstVDIfsOperation)
8397{
8398 return VDCopyEx(pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename,
8399 cbSize, VD_IMAGE_CONTENT_UNKNOWN, VD_IMAGE_CONTENT_UNKNOWN,
8400 uImageFlags, pDstUuid, uOpenFlags, pVDIfsOperation,
8401 pDstVDIfsImage, pDstVDIfsOperation);
8402}
8403
8404/**
8405 * Optimizes the storage consumption of an image. Typically the unused blocks
8406 * have to be wiped with zeroes to achieve a substantial reduced storage use.
8407 * Another optimization done is reordering the image blocks, which can provide
8408 * a significant performance boost, as reads and writes tend to use less random
8409 * file offsets.
8410 *
8411 * @return VBox status code.
8412 * @return VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
8413 * @return VERR_VD_IMAGE_READ_ONLY if image is not writable.
8414 * @return VERR_NOT_SUPPORTED if this kind of image can be compacted, but
8415 * the code for this isn't implemented yet.
8416 * @param pDisk Pointer to HDD container.
8417 * @param nImage Image number, counts from 0. 0 is always base image of container.
8418 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
8419 */
8420VBOXDDU_DECL(int) VDCompact(PVBOXHDD pDisk, unsigned nImage,
8421 PVDINTERFACE pVDIfsOperation)
8422{
8423 int rc = VINF_SUCCESS;
8424 int rc2;
8425 bool fLockRead = false, fLockWrite = false;
8426 void *pvBuf = NULL;
8427 void *pvTmp = NULL;
8428
8429 LogFlowFunc(("pDisk=%#p nImage=%u pVDIfsOperation=%#p\n",
8430 pDisk, nImage, pVDIfsOperation));
8431
8432 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
8433
8434 do {
8435 /* Check arguments. */
8436 AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
8437 rc = VERR_INVALID_PARAMETER);
8438 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE,
8439 ("u32Signature=%08x\n", pDisk->u32Signature));
8440
8441 rc2 = vdThreadStartRead(pDisk);
8442 AssertRC(rc2);
8443 fLockRead = true;
8444
8445 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8446 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
8447
8448 /* If there is no compact callback for not file based backends then
8449 * the backend doesn't need compaction. No need to make much fuss about
8450 * this. For file based ones signal this as not yet supported. */
8451 if (!pImage->Backend->pfnCompact)
8452 {
8453 if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
8454 rc = VERR_NOT_SUPPORTED;
8455 else
8456 rc = VINF_SUCCESS;
8457 break;
8458 }
8459
8460 /* Insert interface for reading parent state into per-operation list,
8461 * if there is a parent image. */
8462 VDINTERFACEPARENTSTATE VDIfParent;
8463 VDPARENTSTATEDESC ParentUser;
8464 if (pImage->pPrev)
8465 {
8466 VDIfParent.pfnParentRead = vdParentRead;
8467 ParentUser.pDisk = pDisk;
8468 ParentUser.pImage = pImage->pPrev;
8469 rc = VDInterfaceAdd(&VDIfParent.Core, "VDCompact_ParentState", VDINTERFACETYPE_PARENTSTATE,
8470 &ParentUser, sizeof(VDINTERFACEPARENTSTATE), &pVDIfsOperation);
8471 AssertRC(rc);
8472 }
8473
8474 rc2 = vdThreadFinishRead(pDisk);
8475 AssertRC(rc2);
8476 fLockRead = false;
8477
8478 rc2 = vdThreadStartWrite(pDisk);
8479 AssertRC(rc2);
8480 fLockWrite = true;
8481
8482 rc = pImage->Backend->pfnCompact(pImage->pBackendData,
8483 0, 99,
8484 pDisk->pVDIfsDisk,
8485 pImage->pVDIfsImage,
8486 pVDIfsOperation);
8487 } while (0);
8488
8489 if (RT_UNLIKELY(fLockWrite))
8490 {
8491 rc2 = vdThreadFinishWrite(pDisk);
8492 AssertRC(rc2);
8493 }
8494 else if (RT_UNLIKELY(fLockRead))
8495 {
8496 rc2 = vdThreadFinishRead(pDisk);
8497 AssertRC(rc2);
8498 }
8499
8500 if (pvBuf)
8501 RTMemTmpFree(pvBuf);
8502 if (pvTmp)
8503 RTMemTmpFree(pvTmp);
8504
8505 if (RT_SUCCESS(rc))
8506 {
8507 if (pIfProgress && pIfProgress->pfnProgress)
8508 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
8509 }
8510
8511 LogFlowFunc(("returns %Rrc\n", rc));
8512 return rc;
8513}
8514
8515/**
8516 * Resizes the given disk image to the given size.
8517 *
8518 * @return VBox status
8519 * @return VERR_VD_IMAGE_READ_ONLY if image is not writable.
8520 * @return VERR_NOT_SUPPORTED if this kind of image can be compacted, but
8521 *
8522 * @param pDisk Pointer to the HDD container.
8523 * @param cbSize New size of the image.
8524 * @param pPCHSGeometry Pointer to the new physical disk geometry <= (16383,16,63). Not NULL.
8525 * @param pLCHSGeometry Pointer to the new logical disk geometry <= (x,255,63). Not NULL.
8526 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
8527 */
8528VBOXDDU_DECL(int) VDResize(PVBOXHDD pDisk, uint64_t cbSize,
8529 PCVDGEOMETRY pPCHSGeometry,
8530 PCVDGEOMETRY pLCHSGeometry,
8531 PVDINTERFACE pVDIfsOperation)
8532{
8533 /** @todo r=klaus resizing was designed to be part of VDCopy, so having a separate function is not desirable. */
8534 int rc = VINF_SUCCESS;
8535 int rc2;
8536 bool fLockRead = false, fLockWrite = false;
8537
8538 LogFlowFunc(("pDisk=%#p cbSize=%llu pVDIfsOperation=%#p\n",
8539 pDisk, cbSize, pVDIfsOperation));
8540
8541 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
8542
8543 do {
8544 /* Check arguments. */
8545 AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
8546 rc = VERR_INVALID_PARAMETER);
8547 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE,
8548 ("u32Signature=%08x\n", pDisk->u32Signature));
8549
8550 rc2 = vdThreadStartRead(pDisk);
8551 AssertRC(rc2);
8552 fLockRead = true;
8553
8554 /* Must have at least one image in the chain, will resize last. */
8555 AssertMsgBreakStmt(pDisk->cImages >= 1, ("cImages=%u\n", pDisk->cImages),
8556 rc = VERR_NOT_SUPPORTED);
8557
8558 PVDIMAGE pImage = pDisk->pLast;
8559
8560 /* If there is no compact callback for not file based backends then
8561 * the backend doesn't need compaction. No need to make much fuss about
8562 * this. For file based ones signal this as not yet supported. */
8563 if (!pImage->Backend->pfnResize)
8564 {
8565 if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
8566 rc = VERR_NOT_SUPPORTED;
8567 else
8568 rc = VINF_SUCCESS;
8569 break;
8570 }
8571
8572 rc2 = vdThreadFinishRead(pDisk);
8573 AssertRC(rc2);
8574 fLockRead = false;
8575
8576 rc2 = vdThreadStartWrite(pDisk);
8577 AssertRC(rc2);
8578 fLockWrite = true;
8579
8580 VDGEOMETRY PCHSGeometryOld;
8581 VDGEOMETRY LCHSGeometryOld;
8582 PCVDGEOMETRY pPCHSGeometryNew;
8583 PCVDGEOMETRY pLCHSGeometryNew;
8584
8585 if (pPCHSGeometry->cCylinders == 0)
8586 {
8587 /* Auto-detect marker, calculate new value ourself. */
8588 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData, &PCHSGeometryOld);
8589 if (RT_SUCCESS(rc) && (PCHSGeometryOld.cCylinders != 0))
8590 PCHSGeometryOld.cCylinders = RT_MIN(cbSize / 512 / PCHSGeometryOld.cHeads / PCHSGeometryOld.cSectors, 16383);
8591 else if (rc == VERR_VD_GEOMETRY_NOT_SET)
8592 rc = VINF_SUCCESS;
8593
8594 pPCHSGeometryNew = &PCHSGeometryOld;
8595 }
8596 else
8597 pPCHSGeometryNew = pPCHSGeometry;
8598
8599 if (pLCHSGeometry->cCylinders == 0)
8600 {
8601 /* Auto-detect marker, calculate new value ourself. */
8602 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData, &LCHSGeometryOld);
8603 if (RT_SUCCESS(rc) && (LCHSGeometryOld.cCylinders != 0))
8604 LCHSGeometryOld.cCylinders = cbSize / 512 / LCHSGeometryOld.cHeads / LCHSGeometryOld.cSectors;
8605 else if (rc == VERR_VD_GEOMETRY_NOT_SET)
8606 rc = VINF_SUCCESS;
8607
8608 pLCHSGeometryNew = &LCHSGeometryOld;
8609 }
8610 else
8611 pLCHSGeometryNew = pLCHSGeometry;
8612
8613 if (RT_SUCCESS(rc))
8614 rc = pImage->Backend->pfnResize(pImage->pBackendData,
8615 cbSize,
8616 pPCHSGeometryNew,
8617 pLCHSGeometryNew,
8618 0, 99,
8619 pDisk->pVDIfsDisk,
8620 pImage->pVDIfsImage,
8621 pVDIfsOperation);
8622 } while (0);
8623
8624 if (RT_UNLIKELY(fLockWrite))
8625 {
8626 rc2 = vdThreadFinishWrite(pDisk);
8627 AssertRC(rc2);
8628 }
8629 else if (RT_UNLIKELY(fLockRead))
8630 {
8631 rc2 = vdThreadFinishRead(pDisk);
8632 AssertRC(rc2);
8633 }
8634
8635 if (RT_SUCCESS(rc))
8636 {
8637 if (pIfProgress && pIfProgress->pfnProgress)
8638 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
8639
8640 pDisk->cbSize = cbSize;
8641 }
8642
8643 LogFlowFunc(("returns %Rrc\n", rc));
8644 return rc;
8645}
8646
8647VBOXDDU_DECL(int) VDPrepareWithFilters(PVBOXHDD pDisk, PVDINTERFACE pVDIfsOperation)
8648{
8649 int rc = VINF_SUCCESS;
8650 int rc2;
8651 bool fLockRead = false, fLockWrite = false;
8652
8653 LogFlowFunc(("pDisk=%#p pVDIfsOperation=%#p\n", pDisk, pVDIfsOperation));
8654
8655 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
8656
8657 do {
8658 /* Check arguments. */
8659 AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
8660 rc = VERR_INVALID_PARAMETER);
8661 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE,
8662 ("u32Signature=%08x\n", pDisk->u32Signature));
8663
8664 rc2 = vdThreadStartRead(pDisk);
8665 AssertRC(rc2);
8666 fLockRead = true;
8667
8668 /* Must have at least one image in the chain. */
8669 AssertMsgBreakStmt(pDisk->cImages >= 1, ("cImages=%u\n", pDisk->cImages),
8670 rc = VERR_VD_NOT_OPENED);
8671
8672 unsigned uOpenFlags = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
8673 AssertMsgBreakStmt(!(uOpenFlags & VD_OPEN_FLAGS_READONLY),
8674 ("Last image should be read write"),
8675 rc = VERR_VD_IMAGE_READ_ONLY);
8676
8677 rc2 = vdThreadFinishRead(pDisk);
8678 AssertRC(rc2);
8679 fLockRead = false;
8680
8681 rc2 = vdThreadStartWrite(pDisk);
8682 AssertRC(rc2);
8683 fLockWrite = true;
8684
8685 /*
8686 * Open all images in the chain in read write mode first to avoid running
8687 * into an error in the middle of the process.
8688 */
8689 PVDIMAGE pImage = pDisk->pBase;
8690
8691 while (pImage)
8692 {
8693 uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
8694 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
8695 {
8696 /*
8697 * Clear skip consistency checks because the image is made writable now and
8698 * skipping consistency checks is only possible for readonly images.
8699 */
8700 uOpenFlags &= ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS);
8701 rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData, uOpenFlags);
8702 if (RT_FAILURE(rc))
8703 break;
8704 }
8705 pImage = pImage->pNext;
8706 }
8707
8708 if (RT_SUCCESS(rc))
8709 {
8710 unsigned cImgCur = 0;
8711 unsigned uPercentStart = 0;
8712 unsigned uPercentSpan = 100 / pDisk->cImages - 1;
8713
8714 /* Allocate tmp buffer. */
8715 void *pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
8716 if (!pvBuf)
8717 {
8718 rc = VERR_NO_MEMORY;
8719 break;
8720 }
8721
8722 pImage = pDisk->pBase;
8723 pDisk->fLocked = true;
8724
8725 while ( pImage
8726 && RT_SUCCESS(rc))
8727 {
8728 /* Get size of image. */
8729 uint64_t cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
8730 uint64_t uOffset = 0;
8731 uint64_t cbRemaining = cbSize;
8732
8733 do
8734 {
8735 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
8736 RTSGSEG SegmentBuf;
8737 RTSGBUF SgBuf;
8738 VDIOCTX IoCtx;
8739
8740 SegmentBuf.pvSeg = pvBuf;
8741 SegmentBuf.cbSeg = VD_MERGE_BUFFER_SIZE;
8742 RTSgBufInit(&SgBuf, &SegmentBuf, 1);
8743 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_READ, 0, 0, NULL,
8744 &SgBuf, NULL, NULL, VDIOCTX_FLAGS_SYNC);
8745
8746 rc = pImage->Backend->pfnRead(pImage->pBackendData, uOffset,
8747 cbThisRead, &IoCtx, &cbThisRead);
8748 if (rc != VERR_VD_BLOCK_FREE)
8749 {
8750 if (RT_FAILURE(rc))
8751 break;
8752
8753 /* Apply filter chains. */
8754 rc = vdFilterChainApplyRead(pDisk, uOffset, cbThisRead, &IoCtx);
8755 if (RT_FAILURE(rc))
8756 break;
8757
8758 rc = vdFilterChainApplyWrite(pDisk, uOffset, cbThisRead, &IoCtx);
8759 if (RT_FAILURE(rc))
8760 break;
8761
8762 RTSgBufReset(&SgBuf);
8763 size_t cbThisWrite = 0;
8764 size_t cbPreRead = 0;
8765 size_t cbPostRead = 0;
8766 rc = pImage->Backend->pfnWrite(pImage->pBackendData, uOffset,
8767 cbThisRead, &IoCtx, &cbThisWrite,
8768 &cbPreRead, &cbPostRead, VD_WRITE_NO_ALLOC);
8769 if (RT_FAILURE(rc))
8770 break;
8771 Assert(cbThisWrite == cbThisRead);
8772 }
8773 else
8774 rc = VINF_SUCCESS;
8775
8776 uOffset += cbThisRead;
8777 cbRemaining -= cbThisRead;
8778
8779 if (pIfProgress && pIfProgress->pfnProgress)
8780 {
8781 rc2 = pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
8782 uPercentStart + uOffset * uPercentSpan / cbSize);
8783 AssertRC(rc2); /* Cancelling this operation without leaving an inconsistent state is not possible. */
8784 }
8785 } while (uOffset < cbSize);
8786
8787 pImage = pImage->pNext;
8788 cImgCur++;
8789 uPercentStart += uPercentSpan;
8790 }
8791
8792 pDisk->fLocked = false;
8793 if (pvBuf)
8794 RTMemTmpFree(pvBuf);
8795 }
8796
8797 /* Change images except last one back to readonly. */
8798 pImage = pDisk->pBase;
8799 while ( pImage != pDisk->pLast
8800 && pImage)
8801 {
8802 uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
8803 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
8804 rc2 = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData, uOpenFlags);
8805 if (RT_FAILURE(rc2))
8806 {
8807 if (RT_SUCCESS(rc))
8808 rc = rc2;
8809 break;
8810 }
8811 pImage = pImage->pNext;
8812 }
8813 } while (0);
8814
8815 if (RT_UNLIKELY(fLockWrite))
8816 {
8817 rc2 = vdThreadFinishWrite(pDisk);
8818 AssertRC(rc2);
8819 }
8820 else if (RT_UNLIKELY(fLockRead))
8821 {
8822 rc2 = vdThreadFinishRead(pDisk);
8823 AssertRC(rc2);
8824 }
8825
8826 if ( RT_SUCCESS(rc)
8827 && pIfProgress
8828 && pIfProgress->pfnProgress)
8829 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
8830
8831 LogFlowFunc(("returns %Rrc\n", rc));
8832 return rc;
8833}
8834
8835/**
8836 * Closes the last opened image file in HDD container.
8837 * If previous image file was opened in read-only mode (the normal case) and
8838 * the last opened image is in read-write mode then the previous image will be
8839 * reopened in read/write mode.
8840 *
8841 * @returns VBox status code.
8842 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
8843 * @param pDisk Pointer to HDD container.
8844 * @param fDelete If true, delete the image from the host disk.
8845 */
8846VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete)
8847{
8848 int rc = VINF_SUCCESS;
8849 int rc2;
8850 bool fLockWrite = false;
8851
8852 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
8853 do
8854 {
8855 /* sanity check */
8856 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8857 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8858
8859 /* Not worth splitting this up into a read lock phase and write
8860 * lock phase, as closing an image is a relatively fast operation
8861 * dominated by the part which needs the write lock. */
8862 rc2 = vdThreadStartWrite(pDisk);
8863 AssertRC(rc2);
8864 fLockWrite = true;
8865
8866 PVDIMAGE pImage = pDisk->pLast;
8867 if (!pImage)
8868 {
8869 rc = VERR_VD_NOT_OPENED;
8870 break;
8871 }
8872
8873 /* Destroy the current discard state first which might still have pending blocks. */
8874 rc = vdDiscardStateDestroy(pDisk);
8875 if (RT_FAILURE(rc))
8876 break;
8877
8878 unsigned uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
8879 /* Remove image from list of opened images. */
8880 vdRemoveImageFromList(pDisk, pImage);
8881 /* Close (and optionally delete) image. */
8882 rc = pImage->Backend->pfnClose(pImage->pBackendData, fDelete);
8883 /* Free remaining resources related to the image. */
8884 RTStrFree(pImage->pszFilename);
8885 RTMemFree(pImage);
8886
8887 pImage = pDisk->pLast;
8888 if (!pImage)
8889 break;
8890
8891 /* If disk was previously in read/write mode, make sure it will stay
8892 * like this (if possible) after closing this image. Set the open flags
8893 * accordingly. */
8894 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
8895 {
8896 uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
8897 uOpenFlags &= ~ VD_OPEN_FLAGS_READONLY;
8898 rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData, uOpenFlags);
8899 }
8900
8901 /* Cache disk information. */
8902 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
8903
8904 /* Cache PCHS geometry. */
8905 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
8906 &pDisk->PCHSGeometry);
8907 if (RT_FAILURE(rc2))
8908 {
8909 pDisk->PCHSGeometry.cCylinders = 0;
8910 pDisk->PCHSGeometry.cHeads = 0;
8911 pDisk->PCHSGeometry.cSectors = 0;
8912 }
8913 else
8914 {
8915 /* Make sure the PCHS geometry is properly clipped. */
8916 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
8917 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
8918 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
8919 }
8920
8921 /* Cache LCHS geometry. */
8922 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
8923 &pDisk->LCHSGeometry);
8924 if (RT_FAILURE(rc2))
8925 {
8926 pDisk->LCHSGeometry.cCylinders = 0;
8927 pDisk->LCHSGeometry.cHeads = 0;
8928 pDisk->LCHSGeometry.cSectors = 0;
8929 }
8930 else
8931 {
8932 /* Make sure the LCHS geometry is properly clipped. */
8933 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
8934 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
8935 }
8936 } while (0);
8937
8938 if (RT_UNLIKELY(fLockWrite))
8939 {
8940 rc2 = vdThreadFinishWrite(pDisk);
8941 AssertRC(rc2);
8942 }
8943
8944 LogFlowFunc(("returns %Rrc\n", rc));
8945 return rc;
8946}
8947
8948/**
8949 * Closes the currently opened cache image file in HDD container.
8950 *
8951 * @return VBox status code.
8952 * @return VERR_VD_NOT_OPENED if no cache is opened in HDD container.
8953 * @param pDisk Pointer to HDD container.
8954 * @param fDelete If true, delete the image from the host disk.
8955 */
8956VBOXDDU_DECL(int) VDCacheClose(PVBOXHDD pDisk, bool fDelete)
8957{
8958 int rc = VINF_SUCCESS;
8959 int rc2;
8960 bool fLockWrite = false;
8961 PVDCACHE pCache = NULL;
8962
8963 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
8964
8965 do
8966 {
8967 /* sanity check */
8968 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8969 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8970
8971 rc2 = vdThreadStartWrite(pDisk);
8972 AssertRC(rc2);
8973 fLockWrite = true;
8974
8975 AssertPtrBreakStmt(pDisk->pCache, rc = VERR_VD_CACHE_NOT_FOUND);
8976
8977 pCache = pDisk->pCache;
8978 pDisk->pCache = NULL;
8979
8980 pCache->Backend->pfnClose(pCache->pBackendData, fDelete);
8981 if (pCache->pszFilename)
8982 RTStrFree(pCache->pszFilename);
8983 RTMemFree(pCache);
8984 } while (0);
8985
8986 if (RT_LIKELY(fLockWrite))
8987 {
8988 rc2 = vdThreadFinishWrite(pDisk);
8989 AssertRC(rc2);
8990 }
8991
8992 LogFlowFunc(("returns %Rrc\n", rc));
8993 return rc;
8994}
8995
8996VBOXDDU_DECL(int) VDFilterRemove(PVBOXHDD pDisk, uint32_t fFlags)
8997{
8998 int rc = VINF_SUCCESS;
8999 int rc2;
9000 bool fLockWrite = false;
9001 PVDFILTER pFilter = NULL;
9002
9003 LogFlowFunc(("pDisk=%#p\n", pDisk));
9004
9005 do
9006 {
9007 /* sanity check */
9008 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9009 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9010
9011 AssertMsgBreakStmt(!(fFlags & ~VD_FILTER_FLAGS_MASK),
9012 ("Invalid flags set (fFlags=%#x)\n", fFlags),
9013 rc = VERR_INVALID_PARAMETER);
9014
9015 rc2 = vdThreadStartWrite(pDisk);
9016 AssertRC(rc2);
9017 fLockWrite = true;
9018
9019 if (fFlags & VD_FILTER_FLAGS_WRITE)
9020 {
9021 AssertBreakStmt(!RTListIsEmpty(&pDisk->ListFilterChainWrite), rc = VERR_VD_NOT_OPENED);
9022 pFilter = RTListGetLast(&pDisk->ListFilterChainWrite, VDFILTER, ListNodeChainWrite);
9023 AssertPtr(pFilter);
9024 RTListNodeRemove(&pFilter->ListNodeChainWrite);
9025 vdFilterRelease(pFilter);
9026 }
9027
9028 if (fFlags & VD_FILTER_FLAGS_READ)
9029 {
9030 AssertBreakStmt(!RTListIsEmpty(&pDisk->ListFilterChainRead), rc = VERR_VD_NOT_OPENED);
9031 pFilter = RTListGetLast(&pDisk->ListFilterChainRead, VDFILTER, ListNodeChainRead);
9032 AssertPtr(pFilter);
9033 RTListNodeRemove(&pFilter->ListNodeChainRead);
9034 vdFilterRelease(pFilter);
9035 }
9036 } while (0);
9037
9038 if (RT_LIKELY(fLockWrite))
9039 {
9040 rc2 = vdThreadFinishWrite(pDisk);
9041 AssertRC(rc2);
9042 }
9043
9044 LogFlowFunc(("returns %Rrc\n", rc));
9045 return rc;
9046}
9047
9048/**
9049 * Closes all opened image files in HDD container.
9050 *
9051 * @returns VBox status code.
9052 * @param pDisk Pointer to HDD container.
9053 */
9054VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk)
9055{
9056 int rc = VINF_SUCCESS;
9057 int rc2;
9058 bool fLockWrite = false;
9059
9060 LogFlowFunc(("pDisk=%#p\n", pDisk));
9061 do
9062 {
9063 /* sanity check */
9064 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9065 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9066
9067 /* Lock the entire operation. */
9068 rc2 = vdThreadStartWrite(pDisk);
9069 AssertRC(rc2);
9070 fLockWrite = true;
9071
9072 PVDCACHE pCache = pDisk->pCache;
9073 if (pCache)
9074 {
9075 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, false);
9076 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
9077 rc = rc2;
9078
9079 if (pCache->pszFilename)
9080 RTStrFree(pCache->pszFilename);
9081 RTMemFree(pCache);
9082 }
9083
9084 PVDIMAGE pImage = pDisk->pLast;
9085 while (VALID_PTR(pImage))
9086 {
9087 PVDIMAGE pPrev = pImage->pPrev;
9088 /* Remove image from list of opened images. */
9089 vdRemoveImageFromList(pDisk, pImage);
9090 /* Close image. */
9091 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, false);
9092 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
9093 rc = rc2;
9094 /* Free remaining resources related to the image. */
9095 RTStrFree(pImage->pszFilename);
9096 RTMemFree(pImage);
9097 pImage = pPrev;
9098 }
9099 Assert(!VALID_PTR(pDisk->pLast));
9100 } while (0);
9101
9102 if (RT_UNLIKELY(fLockWrite))
9103 {
9104 rc2 = vdThreadFinishWrite(pDisk);
9105 AssertRC(rc2);
9106 }
9107
9108 LogFlowFunc(("returns %Rrc\n", rc));
9109 return rc;
9110}
9111
9112/**
9113 * Removes all filters of the given HDD container.
9114 *
9115 * @return VBox status code.
9116 * @param pDisk Pointer to HDD container.
9117 */
9118VBOXDDU_DECL(int) VDFilterRemoveAll(PVBOXHDD pDisk)
9119{
9120 int rc = VINF_SUCCESS;
9121 int rc2;
9122 bool fLockWrite = false;
9123
9124 LogFlowFunc(("pDisk=%#p\n", pDisk));
9125 do
9126 {
9127 /* sanity check */
9128 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9129 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9130
9131 /* Lock the entire operation. */
9132 rc2 = vdThreadStartWrite(pDisk);
9133 AssertRC(rc2);
9134 fLockWrite = true;
9135
9136 PVDFILTER pFilter, pFilterNext;
9137 RTListForEachSafe(&pDisk->ListFilterChainWrite, pFilter, pFilterNext, VDFILTER, ListNodeChainWrite)
9138 {
9139 RTListNodeRemove(&pFilter->ListNodeChainWrite);
9140 vdFilterRelease(pFilter);
9141 }
9142
9143 RTListForEachSafe(&pDisk->ListFilterChainRead, pFilter, pFilterNext, VDFILTER, ListNodeChainRead)
9144 {
9145 RTListNodeRemove(&pFilter->ListNodeChainRead);
9146 vdFilterRelease(pFilter);
9147 }
9148 Assert(RTListIsEmpty(&pDisk->ListFilterChainRead));
9149 Assert(RTListIsEmpty(&pDisk->ListFilterChainWrite));
9150 } while (0);
9151
9152 if (RT_UNLIKELY(fLockWrite))
9153 {
9154 rc2 = vdThreadFinishWrite(pDisk);
9155 AssertRC(rc2);
9156 }
9157
9158 LogFlowFunc(("returns %Rrc\n", rc));
9159 return rc;
9160}
9161
9162/**
9163 * Read data from virtual HDD.
9164 *
9165 * @returns VBox status code.
9166 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
9167 * @param pDisk Pointer to HDD container.
9168 * @param uOffset Offset of first reading byte from start of disk.
9169 * @param pvBuf Pointer to buffer for reading data.
9170 * @param cbRead Number of bytes to read.
9171 */
9172VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuf,
9173 size_t cbRead)
9174{
9175 int rc = VINF_SUCCESS;
9176 int rc2;
9177 bool fLockRead = false;
9178
9179 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbRead=%zu\n",
9180 pDisk, uOffset, pvBuf, cbRead));
9181 do
9182 {
9183 /* sanity check */
9184 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9185 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9186
9187 /* Check arguments. */
9188 AssertMsgBreakStmt(VALID_PTR(pvBuf),
9189 ("pvBuf=%#p\n", pvBuf),
9190 rc = VERR_INVALID_PARAMETER);
9191 AssertMsgBreakStmt(cbRead,
9192 ("cbRead=%zu\n", cbRead),
9193 rc = VERR_INVALID_PARAMETER);
9194
9195 rc2 = vdThreadStartRead(pDisk);
9196 AssertRC(rc2);
9197 fLockRead = true;
9198
9199 PVDIMAGE pImage = pDisk->pLast;
9200 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
9201
9202 if (uOffset + cbRead > pDisk->cbSize)
9203 {
9204 /* Floppy images might be smaller than the standard expected by
9205 the floppy controller code. So, we won't fail here. */
9206 AssertMsgBreakStmt(pDisk->enmType == VDTYPE_FLOPPY,
9207 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
9208 uOffset, cbRead, pDisk->cbSize),
9209 rc = VERR_EOF);
9210 memset(pvBuf, 0xf6, cbRead); /* f6h = format.com filler byte */
9211 if (uOffset >= pDisk->cbSize)
9212 break;
9213 cbRead = pDisk->cbSize - uOffset;
9214 }
9215
9216 rc = vdReadHelper(pDisk, pImage, uOffset, pvBuf, cbRead,
9217 true /* fUpdateCache */);
9218 } while (0);
9219
9220 if (RT_UNLIKELY(fLockRead))
9221 {
9222 rc2 = vdThreadFinishRead(pDisk);
9223 AssertRC(rc2);
9224 }
9225
9226 LogFlowFunc(("returns %Rrc\n", rc));
9227 return rc;
9228}
9229
9230/**
9231 * Write data to virtual HDD.
9232 *
9233 * @returns VBox status code.
9234 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
9235 * @param pDisk Pointer to HDD container.
9236 * @param uOffset Offset of the first byte being
9237 * written from start of disk.
9238 * @param pvBuf Pointer to buffer for writing data.
9239 * @param cbWrite Number of bytes to write.
9240 */
9241VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf,
9242 size_t cbWrite)
9243{
9244 int rc = VINF_SUCCESS;
9245 int rc2;
9246 bool fLockWrite = false;
9247
9248 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbWrite=%zu\n",
9249 pDisk, uOffset, pvBuf, cbWrite));
9250 do
9251 {
9252 /* sanity check */
9253 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9254 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9255
9256 /* Check arguments. */
9257 AssertMsgBreakStmt(VALID_PTR(pvBuf),
9258 ("pvBuf=%#p\n", pvBuf),
9259 rc = VERR_INVALID_PARAMETER);
9260 AssertMsgBreakStmt(cbWrite,
9261 ("cbWrite=%zu\n", cbWrite),
9262 rc = VERR_INVALID_PARAMETER);
9263
9264 rc2 = vdThreadStartWrite(pDisk);
9265 AssertRC(rc2);
9266 fLockWrite = true;
9267
9268 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
9269 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
9270 uOffset, cbWrite, pDisk->cbSize),
9271 rc = VERR_INVALID_PARAMETER);
9272
9273 PVDIMAGE pImage = pDisk->pLast;
9274 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
9275
9276 vdSetModifiedFlag(pDisk);
9277 rc = vdWriteHelper(pDisk, pImage, uOffset, pvBuf, cbWrite,
9278 VDIOCTX_FLAGS_READ_UPDATE_CACHE);
9279 if (RT_FAILURE(rc))
9280 break;
9281
9282 /* If there is a merge (in the direction towards a parent) running
9283 * concurrently then we have to also "relay" the write to this parent,
9284 * as the merge position might be already past the position where
9285 * this write is going. The "context" of the write can come from the
9286 * natural chain, since merging either already did or will take care
9287 * of the "other" content which is might be needed to fill the block
9288 * to a full allocation size. The cache doesn't need to be touched
9289 * as this write is covered by the previous one. */
9290 if (RT_UNLIKELY(pDisk->pImageRelay))
9291 rc = vdWriteHelper(pDisk, pDisk->pImageRelay, uOffset,
9292 pvBuf, cbWrite, VDIOCTX_FLAGS_DEFAULT);
9293 } while (0);
9294
9295 if (RT_UNLIKELY(fLockWrite))
9296 {
9297 rc2 = vdThreadFinishWrite(pDisk);
9298 AssertRC(rc2);
9299 }
9300
9301 LogFlowFunc(("returns %Rrc\n", rc));
9302 return rc;
9303}
9304
9305/**
9306 * Make sure the on disk representation of a virtual HDD is up to date.
9307 *
9308 * @returns VBox status code.
9309 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
9310 * @param pDisk Pointer to HDD container.
9311 */
9312VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
9313{
9314 int rc = VINF_SUCCESS;
9315 int rc2;
9316 bool fLockWrite = false;
9317
9318 LogFlowFunc(("pDisk=%#p\n", pDisk));
9319 do
9320 {
9321 /* sanity check */
9322 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9323 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9324
9325 rc2 = vdThreadStartWrite(pDisk);
9326 AssertRC(rc2);
9327 fLockWrite = true;
9328
9329 PVDIMAGE pImage = pDisk->pLast;
9330 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
9331
9332 VDIOCTX IoCtx;
9333 RTSEMEVENT hEventComplete = NIL_RTSEMEVENT;
9334
9335 rc = RTSemEventCreate(&hEventComplete);
9336 if (RT_FAILURE(rc))
9337 break;
9338
9339 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_FLUSH, 0, 0, pImage, NULL,
9340 NULL, vdFlushHelperAsync, VDIOCTX_FLAGS_SYNC | VDIOCTX_FLAGS_DONT_FREE);
9341
9342 IoCtx.Type.Root.pfnComplete = vdIoCtxSyncComplete;
9343 IoCtx.Type.Root.pvUser1 = pDisk;
9344 IoCtx.Type.Root.pvUser2 = hEventComplete;
9345 rc = vdIoCtxProcessSync(&IoCtx, hEventComplete);
9346
9347 RTSemEventDestroy(hEventComplete);
9348 } while (0);
9349
9350 if (RT_UNLIKELY(fLockWrite))
9351 {
9352 rc2 = vdThreadFinishWrite(pDisk);
9353 AssertRC(rc2);
9354 }
9355
9356 LogFlowFunc(("returns %Rrc\n", rc));
9357 return rc;
9358}
9359
9360/**
9361 * Get number of opened images in HDD container.
9362 *
9363 * @returns Number of opened images for HDD container. 0 if no images have been opened.
9364 * @param pDisk Pointer to HDD container.
9365 */
9366VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk)
9367{
9368 unsigned cImages;
9369 int rc2;
9370 bool fLockRead = false;
9371
9372 LogFlowFunc(("pDisk=%#p\n", pDisk));
9373 do
9374 {
9375 /* sanity check */
9376 AssertPtrBreakStmt(pDisk, cImages = 0);
9377 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9378
9379 rc2 = vdThreadStartRead(pDisk);
9380 AssertRC(rc2);
9381 fLockRead = true;
9382
9383 cImages = pDisk->cImages;
9384 } while (0);
9385
9386 if (RT_UNLIKELY(fLockRead))
9387 {
9388 rc2 = vdThreadFinishRead(pDisk);
9389 AssertRC(rc2);
9390 }
9391
9392 LogFlowFunc(("returns %u\n", cImages));
9393 return cImages;
9394}
9395
9396/**
9397 * Get read/write mode of HDD container.
9398 *
9399 * @returns Virtual disk ReadOnly status.
9400 * @returns true if no image is opened in HDD container.
9401 * @param pDisk Pointer to HDD container.
9402 */
9403VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
9404{
9405 bool fReadOnly;
9406 int rc2;
9407 bool fLockRead = false;
9408
9409 LogFlowFunc(("pDisk=%#p\n", pDisk));
9410 do
9411 {
9412 /* sanity check */
9413 AssertPtrBreakStmt(pDisk, fReadOnly = false);
9414 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9415
9416 rc2 = vdThreadStartRead(pDisk);
9417 AssertRC(rc2);
9418 fLockRead = true;
9419
9420 PVDIMAGE pImage = pDisk->pLast;
9421 AssertPtrBreakStmt(pImage, fReadOnly = true);
9422
9423 unsigned uOpenFlags;
9424 uOpenFlags = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
9425 fReadOnly = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
9426 } while (0);
9427
9428 if (RT_UNLIKELY(fLockRead))
9429 {
9430 rc2 = vdThreadFinishRead(pDisk);
9431 AssertRC(rc2);
9432 }
9433
9434 LogFlowFunc(("returns %d\n", fReadOnly));
9435 return fReadOnly;
9436}
9437
9438/**
9439 * Get sector size of an image in HDD container.
9440 *
9441 * @return Virtual disk sector size in bytes.
9442 * @return 0 if image with specified number was not opened.
9443 * @param pDisk Pointer to HDD container.
9444 * @param nImage Image number, counts from 0. 0 is always base image of container.
9445 */
9446VBOXDDU_DECL(uint32_t) VDGetSectorSize(PVBOXHDD pDisk, unsigned nImage)
9447{
9448 uint64_t cbSector;
9449 int rc2;
9450 bool fLockRead = false;
9451
9452 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
9453 do
9454 {
9455 /* sanity check */
9456 AssertPtrBreakStmt(pDisk, cbSector = 0);
9457 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9458
9459 rc2 = vdThreadStartRead(pDisk);
9460 AssertRC(rc2);
9461 fLockRead = true;
9462
9463 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9464 AssertPtrBreakStmt(pImage, cbSector = 0);
9465 cbSector = pImage->Backend->pfnGetSectorSize(pImage->pBackendData);
9466 } while (0);
9467
9468 if (RT_UNLIKELY(fLockRead))
9469 {
9470 rc2 = vdThreadFinishRead(pDisk);
9471 AssertRC(rc2);
9472 }
9473
9474 LogFlowFunc(("returns %u\n", cbSector));
9475 return cbSector;
9476}
9477
9478/**
9479 * Get total capacity of an image in HDD container.
9480 *
9481 * @returns Virtual disk size in bytes.
9482 * @returns 0 if no image with specified number was not opened.
9483 * @param pDisk Pointer to HDD container.
9484 * @param nImage Image number, counts from 0. 0 is always base image of container.
9485 */
9486VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk, unsigned nImage)
9487{
9488 uint64_t cbSize;
9489 int rc2;
9490 bool fLockRead = false;
9491
9492 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
9493 do
9494 {
9495 /* sanity check */
9496 AssertPtrBreakStmt(pDisk, cbSize = 0);
9497 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9498
9499 rc2 = vdThreadStartRead(pDisk);
9500 AssertRC(rc2);
9501 fLockRead = true;
9502
9503 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9504 AssertPtrBreakStmt(pImage, cbSize = 0);
9505 cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
9506 } while (0);
9507
9508 if (RT_UNLIKELY(fLockRead))
9509 {
9510 rc2 = vdThreadFinishRead(pDisk);
9511 AssertRC(rc2);
9512 }
9513
9514 LogFlowFunc(("returns %llu\n", cbSize));
9515 return cbSize;
9516}
9517
9518/**
9519 * Get total file size of an image in HDD container.
9520 *
9521 * @returns Virtual disk size in bytes.
9522 * @returns 0 if no image is opened in HDD container.
9523 * @param pDisk Pointer to HDD container.
9524 * @param nImage Image number, counts from 0. 0 is always base image of container.
9525 */
9526VBOXDDU_DECL(uint64_t) VDGetFileSize(PVBOXHDD pDisk, unsigned nImage)
9527{
9528 uint64_t cbSize;
9529 int rc2;
9530 bool fLockRead = false;
9531
9532 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
9533 do
9534 {
9535 /* sanity check */
9536 AssertPtrBreakStmt(pDisk, cbSize = 0);
9537 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9538
9539 rc2 = vdThreadStartRead(pDisk);
9540 AssertRC(rc2);
9541 fLockRead = true;
9542
9543 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9544 AssertPtrBreakStmt(pImage, cbSize = 0);
9545 cbSize = pImage->Backend->pfnGetFileSize(pImage->pBackendData);
9546 } while (0);
9547
9548 if (RT_UNLIKELY(fLockRead))
9549 {
9550 rc2 = vdThreadFinishRead(pDisk);
9551 AssertRC(rc2);
9552 }
9553
9554 LogFlowFunc(("returns %llu\n", cbSize));
9555 return cbSize;
9556}
9557
9558/**
9559 * Get virtual disk PCHS geometry stored in HDD container.
9560 *
9561 * @returns VBox status code.
9562 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9563 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
9564 * @param pDisk Pointer to HDD container.
9565 * @param nImage Image number, counts from 0. 0 is always base image of container.
9566 * @param pPCHSGeometry Where to store PCHS geometry. Not NULL.
9567 */
9568VBOXDDU_DECL(int) VDGetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
9569 PVDGEOMETRY pPCHSGeometry)
9570{
9571 int rc = VINF_SUCCESS;
9572 int rc2;
9573 bool fLockRead = false;
9574
9575 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p\n",
9576 pDisk, nImage, pPCHSGeometry));
9577 do
9578 {
9579 /* sanity check */
9580 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9581 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9582
9583 /* Check arguments. */
9584 AssertMsgBreakStmt(VALID_PTR(pPCHSGeometry),
9585 ("pPCHSGeometry=%#p\n", pPCHSGeometry),
9586 rc = VERR_INVALID_PARAMETER);
9587
9588 rc2 = vdThreadStartRead(pDisk);
9589 AssertRC(rc2);
9590 fLockRead = true;
9591
9592 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9593 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9594
9595 if (pImage == pDisk->pLast)
9596 {
9597 /* Use cached information if possible. */
9598 if (pDisk->PCHSGeometry.cCylinders != 0)
9599 *pPCHSGeometry = pDisk->PCHSGeometry;
9600 else
9601 rc = VERR_VD_GEOMETRY_NOT_SET;
9602 }
9603 else
9604 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
9605 pPCHSGeometry);
9606 } while (0);
9607
9608 if (RT_UNLIKELY(fLockRead))
9609 {
9610 rc2 = vdThreadFinishRead(pDisk);
9611 AssertRC(rc2);
9612 }
9613
9614 LogFlowFunc(("%Rrc (PCHS=%u/%u/%u)\n", rc,
9615 pDisk->PCHSGeometry.cCylinders, pDisk->PCHSGeometry.cHeads,
9616 pDisk->PCHSGeometry.cSectors));
9617 return rc;
9618}
9619
9620/**
9621 * Store virtual disk PCHS geometry in HDD container.
9622 *
9623 * Note that in case of unrecoverable error all images in HDD container will be closed.
9624 *
9625 * @returns VBox status code.
9626 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9627 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
9628 * @param pDisk Pointer to HDD container.
9629 * @param nImage Image number, counts from 0. 0 is always base image of container.
9630 * @param pPCHSGeometry Where to load PCHS geometry from. Not NULL.
9631 */
9632VBOXDDU_DECL(int) VDSetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
9633 PCVDGEOMETRY pPCHSGeometry)
9634{
9635 int rc = VINF_SUCCESS;
9636 int rc2;
9637 bool fLockWrite = false;
9638
9639 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
9640 pDisk, nImage, pPCHSGeometry, pPCHSGeometry->cCylinders,
9641 pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
9642 do
9643 {
9644 /* sanity check */
9645 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9646 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9647
9648 /* Check arguments. */
9649 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
9650 && pPCHSGeometry->cHeads <= 16
9651 && pPCHSGeometry->cSectors <= 63,
9652 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
9653 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
9654 pPCHSGeometry->cSectors),
9655 rc = VERR_INVALID_PARAMETER);
9656
9657 rc2 = vdThreadStartWrite(pDisk);
9658 AssertRC(rc2);
9659 fLockWrite = true;
9660
9661 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9662 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9663
9664 if (pImage == pDisk->pLast)
9665 {
9666 if ( pPCHSGeometry->cCylinders != pDisk->PCHSGeometry.cCylinders
9667 || pPCHSGeometry->cHeads != pDisk->PCHSGeometry.cHeads
9668 || pPCHSGeometry->cSectors != pDisk->PCHSGeometry.cSectors)
9669 {
9670 /* Only update geometry if it is changed. Avoids similar checks
9671 * in every backend. Most of the time the new geometry is set
9672 * to the previous values, so no need to go through the hassle
9673 * of updating an image which could be opened in read-only mode
9674 * right now. */
9675 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pBackendData,
9676 pPCHSGeometry);
9677
9678 /* Cache new geometry values in any case. */
9679 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
9680 &pDisk->PCHSGeometry);
9681 if (RT_FAILURE(rc2))
9682 {
9683 pDisk->PCHSGeometry.cCylinders = 0;
9684 pDisk->PCHSGeometry.cHeads = 0;
9685 pDisk->PCHSGeometry.cSectors = 0;
9686 }
9687 else
9688 {
9689 /* Make sure the CHS geometry is properly clipped. */
9690 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 255);
9691 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
9692 }
9693 }
9694 }
9695 else
9696 {
9697 VDGEOMETRY PCHS;
9698 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
9699 &PCHS);
9700 if ( RT_FAILURE(rc)
9701 || pPCHSGeometry->cCylinders != PCHS.cCylinders
9702 || pPCHSGeometry->cHeads != PCHS.cHeads
9703 || pPCHSGeometry->cSectors != PCHS.cSectors)
9704 {
9705 /* Only update geometry if it is changed. Avoids similar checks
9706 * in every backend. Most of the time the new geometry is set
9707 * to the previous values, so no need to go through the hassle
9708 * of updating an image which could be opened in read-only mode
9709 * right now. */
9710 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pBackendData,
9711 pPCHSGeometry);
9712 }
9713 }
9714 } while (0);
9715
9716 if (RT_UNLIKELY(fLockWrite))
9717 {
9718 rc2 = vdThreadFinishWrite(pDisk);
9719 AssertRC(rc2);
9720 }
9721
9722 LogFlowFunc(("returns %Rrc\n", rc));
9723 return rc;
9724}
9725
9726/**
9727 * Get virtual disk LCHS geometry stored in HDD container.
9728 *
9729 * @returns VBox status code.
9730 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9731 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
9732 * @param pDisk Pointer to HDD container.
9733 * @param nImage Image number, counts from 0. 0 is always base image of container.
9734 * @param pLCHSGeometry Where to store LCHS geometry. Not NULL.
9735 */
9736VBOXDDU_DECL(int) VDGetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
9737 PVDGEOMETRY pLCHSGeometry)
9738{
9739 int rc = VINF_SUCCESS;
9740 int rc2;
9741 bool fLockRead = false;
9742
9743 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p\n",
9744 pDisk, nImage, pLCHSGeometry));
9745 do
9746 {
9747 /* sanity check */
9748 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9749 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9750
9751 /* Check arguments. */
9752 AssertMsgBreakStmt(VALID_PTR(pLCHSGeometry),
9753 ("pLCHSGeometry=%#p\n", pLCHSGeometry),
9754 rc = VERR_INVALID_PARAMETER);
9755
9756 rc2 = vdThreadStartRead(pDisk);
9757 AssertRC(rc2);
9758 fLockRead = true;
9759
9760 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9761 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9762
9763 if (pImage == pDisk->pLast)
9764 {
9765 /* Use cached information if possible. */
9766 if (pDisk->LCHSGeometry.cCylinders != 0)
9767 *pLCHSGeometry = pDisk->LCHSGeometry;
9768 else
9769 rc = VERR_VD_GEOMETRY_NOT_SET;
9770 }
9771 else
9772 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
9773 pLCHSGeometry);
9774 } while (0);
9775
9776 if (RT_UNLIKELY(fLockRead))
9777 {
9778 rc2 = vdThreadFinishRead(pDisk);
9779 AssertRC(rc2);
9780 }
9781
9782 LogFlowFunc((": %Rrc (LCHS=%u/%u/%u)\n", rc,
9783 pDisk->LCHSGeometry.cCylinders, pDisk->LCHSGeometry.cHeads,
9784 pDisk->LCHSGeometry.cSectors));
9785 return rc;
9786}
9787
9788/**
9789 * Store virtual disk LCHS geometry in HDD container.
9790 *
9791 * Note that in case of unrecoverable error all images in HDD container will be closed.
9792 *
9793 * @returns VBox status code.
9794 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9795 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
9796 * @param pDisk Pointer to HDD container.
9797 * @param nImage Image number, counts from 0. 0 is always base image of container.
9798 * @param pLCHSGeometry Where to load LCHS geometry from. Not NULL.
9799 */
9800VBOXDDU_DECL(int) VDSetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
9801 PCVDGEOMETRY pLCHSGeometry)
9802{
9803 int rc = VINF_SUCCESS;
9804 int rc2;
9805 bool fLockWrite = false;
9806
9807 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
9808 pDisk, nImage, pLCHSGeometry, pLCHSGeometry->cCylinders,
9809 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
9810 do
9811 {
9812 /* sanity check */
9813 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9814 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9815
9816 /* Check arguments. */
9817 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
9818 && pLCHSGeometry->cHeads <= 255
9819 && pLCHSGeometry->cSectors <= 63,
9820 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
9821 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
9822 pLCHSGeometry->cSectors),
9823 rc = VERR_INVALID_PARAMETER);
9824
9825 rc2 = vdThreadStartWrite(pDisk);
9826 AssertRC(rc2);
9827 fLockWrite = true;
9828
9829 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9830 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9831
9832 if (pImage == pDisk->pLast)
9833 {
9834 if ( pLCHSGeometry->cCylinders != pDisk->LCHSGeometry.cCylinders
9835 || pLCHSGeometry->cHeads != pDisk->LCHSGeometry.cHeads
9836 || pLCHSGeometry->cSectors != pDisk->LCHSGeometry.cSectors)
9837 {
9838 /* Only update geometry if it is changed. Avoids similar checks
9839 * in every backend. Most of the time the new geometry is set
9840 * to the previous values, so no need to go through the hassle
9841 * of updating an image which could be opened in read-only mode
9842 * right now. */
9843 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pBackendData,
9844 pLCHSGeometry);
9845
9846 /* Cache new geometry values in any case. */
9847 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
9848 &pDisk->LCHSGeometry);
9849 if (RT_FAILURE(rc2))
9850 {
9851 pDisk->LCHSGeometry.cCylinders = 0;
9852 pDisk->LCHSGeometry.cHeads = 0;
9853 pDisk->LCHSGeometry.cSectors = 0;
9854 }
9855 else
9856 {
9857 /* Make sure the CHS geometry is properly clipped. */
9858 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
9859 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
9860 }
9861 }
9862 }
9863 else
9864 {
9865 VDGEOMETRY LCHS;
9866 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
9867 &LCHS);
9868 if ( RT_FAILURE(rc)
9869 || pLCHSGeometry->cCylinders != LCHS.cCylinders
9870 || pLCHSGeometry->cHeads != LCHS.cHeads
9871 || pLCHSGeometry->cSectors != LCHS.cSectors)
9872 {
9873 /* Only update geometry if it is changed. Avoids similar checks
9874 * in every backend. Most of the time the new geometry is set
9875 * to the previous values, so no need to go through the hassle
9876 * of updating an image which could be opened in read-only mode
9877 * right now. */
9878 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pBackendData,
9879 pLCHSGeometry);
9880 }
9881 }
9882 } while (0);
9883
9884 if (RT_UNLIKELY(fLockWrite))
9885 {
9886 rc2 = vdThreadFinishWrite(pDisk);
9887 AssertRC(rc2);
9888 }
9889
9890 LogFlowFunc(("returns %Rrc\n", rc));
9891 return rc;
9892}
9893
9894/**
9895 * Get version of image in HDD container.
9896 *
9897 * @returns VBox status code.
9898 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9899 * @param pDisk Pointer to HDD container.
9900 * @param nImage Image number, counts from 0. 0 is always base image of container.
9901 * @param puVersion Where to store the image version.
9902 */
9903VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
9904 unsigned *puVersion)
9905{
9906 int rc = VINF_SUCCESS;
9907 int rc2;
9908 bool fLockRead = false;
9909
9910 LogFlowFunc(("pDisk=%#p nImage=%u puVersion=%#p\n",
9911 pDisk, nImage, puVersion));
9912 do
9913 {
9914 /* sanity check */
9915 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9916 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9917
9918 /* Check arguments. */
9919 AssertMsgBreakStmt(VALID_PTR(puVersion),
9920 ("puVersion=%#p\n", puVersion),
9921 rc = VERR_INVALID_PARAMETER);
9922
9923 rc2 = vdThreadStartRead(pDisk);
9924 AssertRC(rc2);
9925 fLockRead = true;
9926
9927 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9928 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9929
9930 *puVersion = pImage->Backend->pfnGetVersion(pImage->pBackendData);
9931 } while (0);
9932
9933 if (RT_UNLIKELY(fLockRead))
9934 {
9935 rc2 = vdThreadFinishRead(pDisk);
9936 AssertRC(rc2);
9937 }
9938
9939 LogFlowFunc(("returns %Rrc uVersion=%#x\n", rc, *puVersion));
9940 return rc;
9941}
9942
9943/**
9944 * List the capabilities of image backend in HDD container.
9945 *
9946 * @returns VBox status code.
9947 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9948 * @param pDisk Pointer to the HDD container.
9949 * @param nImage Image number, counts from 0. 0 is always base image of container.
9950 * @param pbackendInfo Where to store the backend information.
9951 */
9952VBOXDDU_DECL(int) VDBackendInfoSingle(PVBOXHDD pDisk, unsigned nImage,
9953 PVDBACKENDINFO pBackendInfo)
9954{
9955 int rc = VINF_SUCCESS;
9956 int rc2;
9957 bool fLockRead = false;
9958
9959 LogFlowFunc(("pDisk=%#p nImage=%u pBackendInfo=%#p\n",
9960 pDisk, nImage, pBackendInfo));
9961 do
9962 {
9963 /* sanity check */
9964 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9965 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9966
9967 /* Check arguments. */
9968 AssertMsgBreakStmt(VALID_PTR(pBackendInfo),
9969 ("pBackendInfo=%#p\n", pBackendInfo),
9970 rc = VERR_INVALID_PARAMETER);
9971
9972 rc2 = vdThreadStartRead(pDisk);
9973 AssertRC(rc2);
9974 fLockRead = true;
9975
9976 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9977 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9978
9979 pBackendInfo->pszBackend = pImage->Backend->pszBackendName;
9980 pBackendInfo->uBackendCaps = pImage->Backend->uBackendCaps;
9981 pBackendInfo->paFileExtensions = pImage->Backend->paFileExtensions;
9982 pBackendInfo->paConfigInfo = pImage->Backend->paConfigInfo;
9983 } while (0);
9984
9985 if (RT_UNLIKELY(fLockRead))
9986 {
9987 rc2 = vdThreadFinishRead(pDisk);
9988 AssertRC(rc2);
9989 }
9990
9991 LogFlowFunc(("returns %Rrc\n", rc));
9992 return rc;
9993}
9994
9995/**
9996 * Get flags of image in HDD container.
9997 *
9998 * @returns VBox status code.
9999 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
10000 * @param pDisk Pointer to HDD container.
10001 * @param nImage Image number, counts from 0. 0 is always base image of container.
10002 * @param puImageFlags Where to store the image flags.
10003 */
10004VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage,
10005 unsigned *puImageFlags)
10006{
10007 int rc = VINF_SUCCESS;
10008 int rc2;
10009 bool fLockRead = false;
10010
10011 LogFlowFunc(("pDisk=%#p nImage=%u puImageFlags=%#p\n",
10012 pDisk, nImage, puImageFlags));
10013 do
10014 {
10015 /* sanity check */
10016 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10017 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10018
10019 /* Check arguments. */
10020 AssertMsgBreakStmt(VALID_PTR(puImageFlags),
10021 ("puImageFlags=%#p\n", puImageFlags),
10022 rc = VERR_INVALID_PARAMETER);
10023
10024 rc2 = vdThreadStartRead(pDisk);
10025 AssertRC(rc2);
10026 fLockRead = true;
10027
10028 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
10029 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
10030
10031 *puImageFlags = pImage->uImageFlags;
10032 } while (0);
10033
10034 if (RT_UNLIKELY(fLockRead))
10035 {
10036 rc2 = vdThreadFinishRead(pDisk);
10037 AssertRC(rc2);
10038 }
10039
10040 LogFlowFunc(("returns %Rrc uImageFlags=%#x\n", rc, *puImageFlags));
10041 return rc;
10042}
10043
10044/**
10045 * Get open flags of image in HDD container.
10046 *
10047 * @returns VBox status code.
10048 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
10049 * @param pDisk Pointer to HDD container.
10050 * @param nImage Image number, counts from 0. 0 is always base image of container.
10051 * @param puOpenFlags Where to store the image open flags.
10052 */
10053VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
10054 unsigned *puOpenFlags)
10055{
10056 int rc = VINF_SUCCESS;
10057 int rc2;
10058 bool fLockRead = false;
10059
10060 LogFlowFunc(("pDisk=%#p nImage=%u puOpenFlags=%#p\n",
10061 pDisk, nImage, puOpenFlags));
10062 do
10063 {
10064 /* sanity check */
10065 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10066 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10067
10068 /* Check arguments. */
10069 AssertMsgBreakStmt(VALID_PTR(puOpenFlags),
10070 ("puOpenFlags=%#p\n", puOpenFlags),
10071 rc = VERR_INVALID_PARAMETER);
10072
10073 rc2 = vdThreadStartRead(pDisk);
10074 AssertRC(rc2);
10075 fLockRead = true;
10076
10077 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
10078 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
10079
10080 *puOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
10081 } while (0);
10082
10083 if (RT_UNLIKELY(fLockRead))
10084 {
10085 rc2 = vdThreadFinishRead(pDisk);
10086 AssertRC(rc2);
10087 }
10088
10089 LogFlowFunc(("returns %Rrc uOpenFlags=%#x\n", rc, *puOpenFlags));
10090 return rc;
10091}
10092
10093/**
10094 * Set open flags of image in HDD container.
10095 * This operation may cause file locking changes and/or files being reopened.
10096 * Note that in case of unrecoverable error all images in HDD container will be closed.
10097 *
10098 * @returns VBox status code.
10099 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
10100 * @param pDisk Pointer to HDD container.
10101 * @param nImage Image number, counts from 0. 0 is always base image of container.
10102 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
10103 */
10104VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
10105 unsigned uOpenFlags)
10106{
10107 int rc;
10108 int rc2;
10109 bool fLockWrite = false;
10110
10111 LogFlowFunc(("pDisk=%#p uOpenFlags=%#u\n", pDisk, uOpenFlags));
10112 do
10113 {
10114 /* sanity check */
10115 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10116 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10117
10118 /* Check arguments. */
10119 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
10120 ("uOpenFlags=%#x\n", uOpenFlags),
10121 rc = VERR_INVALID_PARAMETER);
10122
10123 rc2 = vdThreadStartWrite(pDisk);
10124 AssertRC(rc2);
10125 fLockWrite = true;
10126
10127 /* Destroy any discard state because the image might be changed to readonly mode. */
10128 rc = vdDiscardStateDestroy(pDisk);
10129 if (RT_FAILURE(rc))
10130 break;
10131
10132 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
10133 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
10134
10135 rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData,
10136 uOpenFlags & ~(VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_IGNORE_FLUSH | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS));
10137 if (RT_SUCCESS(rc))
10138 pImage->uOpenFlags = uOpenFlags & (VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_DISCARD | VD_OPEN_FLAGS_IGNORE_FLUSH | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS);
10139 } while (0);
10140
10141 if (RT_UNLIKELY(fLockWrite))
10142 {
10143 rc2 = vdThreadFinishWrite(pDisk);
10144 AssertRC(rc2);
10145 }
10146
10147 LogFlowFunc(("returns %Rrc\n", rc));
10148 return rc;
10149}
10150
10151/**
10152 * Get base filename of image in HDD container. Some image formats use
10153 * other filenames as well, so don't use this for anything but informational
10154 * purposes.
10155 *
10156 * @returns VBox status code.
10157 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
10158 * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
10159 * @param pDisk Pointer to HDD container.
10160 * @param nImage Image number, counts from 0. 0 is always base image of container.
10161 * @param pszFilename Where to store the image file name.
10162 * @param cbFilename Size of buffer pszFilename points to.
10163 */
10164VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
10165 char *pszFilename, unsigned cbFilename)
10166{
10167 int rc;
10168 int rc2;
10169 bool fLockRead = false;
10170
10171 LogFlowFunc(("pDisk=%#p nImage=%u pszFilename=%#p cbFilename=%u\n",
10172 pDisk, nImage, pszFilename, cbFilename));
10173 do
10174 {
10175 /* sanity check */
10176 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10177 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10178
10179 /* Check arguments. */
10180 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
10181 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
10182 rc = VERR_INVALID_PARAMETER);
10183 AssertMsgBreakStmt(cbFilename,
10184 ("cbFilename=%u\n", cbFilename),
10185 rc = VERR_INVALID_PARAMETER);
10186
10187 rc2 = vdThreadStartRead(pDisk);
10188 AssertRC(rc2);
10189 fLockRead = true;
10190
10191 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
10192 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
10193
10194 size_t cb = strlen(pImage->pszFilename);
10195 if (cb <= cbFilename)
10196 {
10197 strcpy(pszFilename, pImage->pszFilename);
10198 rc = VINF_SUCCESS;
10199 }
10200 else
10201 {
10202 strncpy(pszFilename, pImage->pszFilename, cbFilename - 1);
10203 pszFilename[cbFilename - 1] = '\0';
10204 rc = VERR_BUFFER_OVERFLOW;
10205 }
10206 } while (0);
10207
10208 if (RT_UNLIKELY(fLockRead))
10209 {
10210 rc2 = vdThreadFinishRead(pDisk);
10211 AssertRC(rc2);
10212 }
10213
10214 LogFlowFunc(("returns %Rrc, pszFilename=\"%s\"\n", rc, pszFilename));
10215 return rc;
10216}
10217
10218/**
10219 * Get the comment line of image in HDD container.
10220 *
10221 * @returns VBox status code.
10222 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
10223 * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
10224 * @param pDisk Pointer to HDD container.
10225 * @param nImage Image number, counts from 0. 0 is always base image of container.
10226 * @param pszComment Where to store the comment string of image. NULL is ok.
10227 * @param cbComment The size of pszComment buffer. 0 is ok.
10228 */
10229VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
10230 char *pszComment, unsigned cbComment)
10231{
10232 int rc;
10233 int rc2;
10234 bool fLockRead = false;
10235
10236 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p cbComment=%u\n",
10237 pDisk, nImage, pszComment, cbComment));
10238 do
10239 {
10240 /* sanity check */
10241 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10242 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10243
10244 /* Check arguments. */
10245 AssertMsgBreakStmt(VALID_PTR(pszComment),
10246 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
10247 rc = VERR_INVALID_PARAMETER);
10248 AssertMsgBreakStmt(cbComment,
10249 ("cbComment=%u\n", cbComment),
10250 rc = VERR_INVALID_PARAMETER);
10251
10252 rc2 = vdThreadStartRead(pDisk);
10253 AssertRC(rc2);
10254 fLockRead = true;
10255
10256 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
10257 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
10258
10259 rc = pImage->Backend->pfnGetComment(pImage->pBackendData, pszComment,
10260 cbComment);
10261 } while (0);
10262
10263 if (RT_UNLIKELY(fLockRead))
10264 {
10265 rc2 = vdThreadFinishRead(pDisk);
10266 AssertRC(rc2);
10267 }
10268
10269 LogFlowFunc(("returns %Rrc, pszComment=\"%s\"\n", rc, pszComment));
10270 return rc;
10271}
10272
10273/**
10274 * Changes the comment line of image in HDD container.
10275 *
10276 * @returns VBox status code.
10277 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
10278 * @param pDisk Pointer to HDD container.
10279 * @param nImage Image number, counts from 0. 0 is always base image of container.
10280 * @param pszComment New comment string (UTF-8). NULL is allowed to reset the comment.
10281 */
10282VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
10283 const char *pszComment)
10284{
10285 int rc;
10286 int rc2;
10287 bool fLockWrite = false;
10288
10289 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p \"%s\"\n",
10290 pDisk, nImage, pszComment, pszComment));
10291 do
10292 {
10293 /* sanity check */
10294 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10295 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10296
10297 /* Check arguments. */
10298 AssertMsgBreakStmt(VALID_PTR(pszComment) || pszComment == NULL,
10299 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
10300 rc = VERR_INVALID_PARAMETER);
10301
10302 rc2 = vdThreadStartWrite(pDisk);
10303 AssertRC(rc2);
10304 fLockWrite = true;
10305
10306 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
10307 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
10308
10309 rc = pImage->Backend->pfnSetComment(pImage->pBackendData, pszComment);
10310 } while (0);
10311
10312 if (RT_UNLIKELY(fLockWrite))
10313 {
10314 rc2 = vdThreadFinishWrite(pDisk);
10315 AssertRC(rc2);
10316 }
10317
10318 LogFlowFunc(("returns %Rrc\n", rc));
10319 return rc;
10320}
10321
10322
10323/**
10324 * Get UUID of image in HDD container.
10325 *
10326 * @returns VBox status code.
10327 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
10328 * @param pDisk Pointer to HDD container.
10329 * @param nImage Image number, counts from 0. 0 is always base image of container.
10330 * @param pUuid Where to store the image creation UUID.
10331 */
10332VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
10333{
10334 int rc;
10335 int rc2;
10336 bool fLockRead = false;
10337
10338 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
10339 do
10340 {
10341 /* sanity check */
10342 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10343 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10344
10345 /* Check arguments. */
10346 AssertMsgBreakStmt(VALID_PTR(pUuid),
10347 ("pUuid=%#p\n", pUuid),
10348 rc = VERR_INVALID_PARAMETER);
10349
10350 rc2 = vdThreadStartRead(pDisk);
10351 AssertRC(rc2);
10352 fLockRead = true;
10353
10354 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
10355 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
10356
10357 rc = pImage->Backend->pfnGetUuid(pImage->pBackendData, pUuid);
10358 } while (0);
10359
10360 if (RT_UNLIKELY(fLockRead))
10361 {
10362 rc2 = vdThreadFinishRead(pDisk);
10363 AssertRC(rc2);
10364 }
10365
10366 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
10367 return rc;
10368}
10369
10370/**
10371 * Set the image's UUID. Should not be used by normal applications.
10372 *
10373 * @returns VBox status code.
10374 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
10375 * @param pDisk Pointer to HDD container.
10376 * @param nImage Image number, counts from 0. 0 is always base image of container.
10377 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
10378 */
10379VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
10380{
10381 int rc;
10382 int rc2;
10383 bool fLockWrite = false;
10384
10385 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
10386 pDisk, nImage, pUuid, pUuid));
10387 do
10388 {
10389 /* sanity check */
10390 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10391 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10392
10393 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
10394 ("pUuid=%#p\n", pUuid),
10395 rc = VERR_INVALID_PARAMETER);
10396
10397 rc2 = vdThreadStartWrite(pDisk);
10398 AssertRC(rc2);
10399 fLockWrite = true;
10400
10401 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
10402 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
10403
10404 RTUUID Uuid;
10405 if (!pUuid)
10406 {
10407 RTUuidCreate(&Uuid);
10408 pUuid = &Uuid;
10409 }
10410 rc = pImage->Backend->pfnSetUuid(pImage->pBackendData, pUuid);
10411 } while (0);
10412
10413 if (RT_UNLIKELY(fLockWrite))
10414 {
10415 rc2 = vdThreadFinishWrite(pDisk);
10416 AssertRC(rc2);
10417 }
10418
10419 LogFlowFunc(("returns %Rrc\n", rc));
10420 return rc;
10421}
10422
10423/**
10424 * Get last modification UUID of image in HDD container.
10425 *
10426 * @returns VBox status code.
10427 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
10428 * @param pDisk Pointer to HDD container.
10429 * @param nImage Image number, counts from 0. 0 is always base image of container.
10430 * @param pUuid Where to store the image modification UUID.
10431 */
10432VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
10433{
10434 int rc = VINF_SUCCESS;
10435 int rc2;
10436 bool fLockRead = false;
10437
10438 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
10439 do
10440 {
10441 /* sanity check */
10442 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10443 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10444
10445 /* Check arguments. */
10446 AssertMsgBreakStmt(VALID_PTR(pUuid),
10447 ("pUuid=%#p\n", pUuid),
10448 rc = VERR_INVALID_PARAMETER);
10449
10450 rc2 = vdThreadStartRead(pDisk);
10451 AssertRC(rc2);
10452 fLockRead = true;
10453
10454 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
10455 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
10456
10457 rc = pImage->Backend->pfnGetModificationUuid(pImage->pBackendData,
10458 pUuid);
10459 } while (0);
10460
10461 if (RT_UNLIKELY(fLockRead))
10462 {
10463 rc2 = vdThreadFinishRead(pDisk);
10464 AssertRC(rc2);
10465 }
10466
10467 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
10468 return rc;
10469}
10470
10471/**
10472 * Set the image's last modification UUID. Should not be used by normal applications.
10473 *
10474 * @returns VBox status code.
10475 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
10476 * @param pDisk Pointer to HDD container.
10477 * @param nImage Image number, counts from 0. 0 is always base image of container.
10478 * @param pUuid New modification UUID of the image. If NULL, a new UUID is created.
10479 */
10480VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
10481{
10482 int rc;
10483 int rc2;
10484 bool fLockWrite = false;
10485
10486 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
10487 pDisk, nImage, pUuid, pUuid));
10488 do
10489 {
10490 /* sanity check */
10491 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10492 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10493
10494 /* Check arguments. */
10495 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
10496 ("pUuid=%#p\n", pUuid),
10497 rc = VERR_INVALID_PARAMETER);
10498
10499 rc2 = vdThreadStartWrite(pDisk);
10500 AssertRC(rc2);
10501 fLockWrite = true;
10502
10503 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
10504 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
10505
10506 RTUUID Uuid;
10507 if (!pUuid)
10508 {
10509 RTUuidCreate(&Uuid);
10510 pUuid = &Uuid;
10511 }
10512 rc = pImage->Backend->pfnSetModificationUuid(pImage->pBackendData,
10513 pUuid);
10514 } while (0);
10515
10516 if (RT_UNLIKELY(fLockWrite))
10517 {
10518 rc2 = vdThreadFinishWrite(pDisk);
10519 AssertRC(rc2);
10520 }
10521
10522 LogFlowFunc(("returns %Rrc\n", rc));
10523 return rc;
10524}
10525
10526/**
10527 * Get parent UUID of image in HDD container.
10528 *
10529 * @returns VBox status code.
10530 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
10531 * @param pDisk Pointer to HDD container.
10532 * @param nImage Image number, counts from 0. 0 is always base image of container.
10533 * @param pUuid Where to store the parent image UUID.
10534 */
10535VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
10536 PRTUUID pUuid)
10537{
10538 int rc = VINF_SUCCESS;
10539 int rc2;
10540 bool fLockRead = false;
10541
10542 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
10543 do
10544 {
10545 /* sanity check */
10546 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10547 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10548
10549 /* Check arguments. */
10550 AssertMsgBreakStmt(VALID_PTR(pUuid),
10551 ("pUuid=%#p\n", pUuid),
10552 rc = VERR_INVALID_PARAMETER);
10553
10554 rc2 = vdThreadStartRead(pDisk);
10555 AssertRC(rc2);
10556 fLockRead = true;
10557
10558 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
10559 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
10560
10561 rc = pImage->Backend->pfnGetParentUuid(pImage->pBackendData, pUuid);
10562 } while (0);
10563
10564 if (RT_UNLIKELY(fLockRead))
10565 {
10566 rc2 = vdThreadFinishRead(pDisk);
10567 AssertRC(rc2);
10568 }
10569
10570 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
10571 return rc;
10572}
10573
10574/**
10575 * Set the image's parent UUID. Should not be used by normal applications.
10576 *
10577 * @returns VBox status code.
10578 * @param pDisk Pointer to HDD container.
10579 * @param nImage Image number, counts from 0. 0 is always base image of container.
10580 * @param pUuid New parent UUID of the image. If NULL, a new UUID is created.
10581 */
10582VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
10583 PCRTUUID pUuid)
10584{
10585 int rc;
10586 int rc2;
10587 bool fLockWrite = false;
10588
10589 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
10590 pDisk, nImage, pUuid, pUuid));
10591 do
10592 {
10593 /* sanity check */
10594 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10595 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10596
10597 /* Check arguments. */
10598 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
10599 ("pUuid=%#p\n", pUuid),
10600 rc = VERR_INVALID_PARAMETER);
10601
10602 rc2 = vdThreadStartWrite(pDisk);
10603 AssertRC(rc2);
10604 fLockWrite = true;
10605
10606 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
10607 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
10608
10609 RTUUID Uuid;
10610 if (!pUuid)
10611 {
10612 RTUuidCreate(&Uuid);
10613 pUuid = &Uuid;
10614 }
10615 rc = pImage->Backend->pfnSetParentUuid(pImage->pBackendData, pUuid);
10616 } while (0);
10617
10618 if (RT_UNLIKELY(fLockWrite))
10619 {
10620 rc2 = vdThreadFinishWrite(pDisk);
10621 AssertRC(rc2);
10622 }
10623
10624 LogFlowFunc(("returns %Rrc\n", rc));
10625 return rc;
10626}
10627
10628
10629/**
10630 * Debug helper - dumps all opened images in HDD container into the log file.
10631 *
10632 * @param pDisk Pointer to HDD container.
10633 */
10634VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk)
10635{
10636 int rc2;
10637 bool fLockRead = false;
10638
10639 do
10640 {
10641 /* sanity check */
10642 AssertPtrBreak(pDisk);
10643 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10644
10645 if (!pDisk->pInterfaceError || !VALID_PTR(pDisk->pInterfaceError->pfnMessage))
10646 pDisk->pInterfaceError->pfnMessage = vdLogMessage;
10647
10648 rc2 = vdThreadStartRead(pDisk);
10649 AssertRC(rc2);
10650 fLockRead = true;
10651
10652 vdMessageWrapper(pDisk, "--- Dumping VD Disk, Images=%u\n", pDisk->cImages);
10653 for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
10654 {
10655 vdMessageWrapper(pDisk, "Dumping VD image \"%s\" (Backend=%s)\n",
10656 pImage->pszFilename, pImage->Backend->pszBackendName);
10657 pImage->Backend->pfnDump(pImage->pBackendData);
10658 }
10659 } while (0);
10660
10661 if (RT_UNLIKELY(fLockRead))
10662 {
10663 rc2 = vdThreadFinishRead(pDisk);
10664 AssertRC(rc2);
10665 }
10666}
10667
10668
10669VBOXDDU_DECL(int) VDDiscardRanges(PVBOXHDD pDisk, PCRTRANGE paRanges, unsigned cRanges)
10670{
10671 int rc;
10672 int rc2;
10673 bool fLockWrite = false;
10674
10675 LogFlowFunc(("pDisk=%#p paRanges=%#p cRanges=%u\n",
10676 pDisk, paRanges, cRanges));
10677 do
10678 {
10679 /* sanity check */
10680 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10681 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10682
10683 /* Check arguments. */
10684 AssertMsgBreakStmt(cRanges,
10685 ("cRanges=%u\n", cRanges),
10686 rc = VERR_INVALID_PARAMETER);
10687 AssertMsgBreakStmt(VALID_PTR(paRanges),
10688 ("paRanges=%#p\n", paRanges),
10689 rc = VERR_INVALID_PARAMETER);
10690
10691 rc2 = vdThreadStartWrite(pDisk);
10692 AssertRC(rc2);
10693 fLockWrite = true;
10694
10695 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
10696
10697 AssertMsgBreakStmt(pDisk->pLast->uOpenFlags & VD_OPEN_FLAGS_DISCARD,
10698 ("Discarding not supported\n"),
10699 rc = VERR_NOT_SUPPORTED);
10700
10701 VDIOCTX IoCtx;
10702 RTSEMEVENT hEventComplete = NIL_RTSEMEVENT;
10703
10704 rc = RTSemEventCreate(&hEventComplete);
10705 if (RT_FAILURE(rc))
10706 break;
10707
10708 vdIoCtxDiscardInit(&IoCtx, pDisk, paRanges, cRanges,
10709 vdIoCtxSyncComplete, pDisk, hEventComplete, NULL,
10710 vdDiscardHelperAsync, VDIOCTX_FLAGS_SYNC | VDIOCTX_FLAGS_DONT_FREE);
10711 rc = vdIoCtxProcessSync(&IoCtx, hEventComplete);
10712
10713 RTSemEventDestroy(hEventComplete);
10714 } while (0);
10715
10716 if (RT_UNLIKELY(fLockWrite))
10717 {
10718 rc2 = vdThreadFinishWrite(pDisk);
10719 AssertRC(rc2);
10720 }
10721
10722 LogFlowFunc(("returns %Rrc\n", rc));
10723 return rc;
10724}
10725
10726
10727VBOXDDU_DECL(int) VDAsyncRead(PVBOXHDD pDisk, uint64_t uOffset, size_t cbRead,
10728 PCRTSGBUF pcSgBuf,
10729 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
10730 void *pvUser1, void *pvUser2)
10731{
10732 int rc = VERR_VD_BLOCK_FREE;
10733 int rc2;
10734 bool fLockRead = false;
10735 PVDIOCTX pIoCtx = NULL;
10736
10737 LogFlowFunc(("pDisk=%#p uOffset=%llu pcSgBuf=%#p cbRead=%zu pvUser1=%#p pvUser2=%#p\n",
10738 pDisk, uOffset, pcSgBuf, cbRead, pvUser1, pvUser2));
10739
10740 do
10741 {
10742 /* sanity check */
10743 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10744 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10745
10746 /* Check arguments. */
10747 AssertMsgBreakStmt(cbRead,
10748 ("cbRead=%zu\n", cbRead),
10749 rc = VERR_INVALID_PARAMETER);
10750 AssertMsgBreakStmt(VALID_PTR(pcSgBuf),
10751 ("pcSgBuf=%#p\n", pcSgBuf),
10752 rc = VERR_INVALID_PARAMETER);
10753
10754 rc2 = vdThreadStartRead(pDisk);
10755 AssertRC(rc2);
10756 fLockRead = true;
10757
10758 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
10759 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
10760 uOffset, cbRead, pDisk->cbSize),
10761 rc = VERR_INVALID_PARAMETER);
10762 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
10763
10764 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_READ, uOffset,
10765 cbRead, pDisk->pLast, pcSgBuf,
10766 pfnComplete, pvUser1, pvUser2,
10767 NULL, vdReadHelperAsync,
10768 VDIOCTX_FLAGS_ZERO_FREE_BLOCKS);
10769 if (!pIoCtx)
10770 {
10771 rc = VERR_NO_MEMORY;
10772 break;
10773 }
10774
10775 rc = vdIoCtxProcessTryLockDefer(pIoCtx);
10776 if (rc == VINF_VD_ASYNC_IO_FINISHED)
10777 {
10778 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
10779 vdIoCtxFree(pDisk, pIoCtx);
10780 else
10781 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
10782 }
10783 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
10784 vdIoCtxFree(pDisk, pIoCtx);
10785
10786 } while (0);
10787
10788 if (RT_UNLIKELY(fLockRead) && ( rc == VINF_VD_ASYNC_IO_FINISHED
10789 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
10790 {
10791 rc2 = vdThreadFinishRead(pDisk);
10792 AssertRC(rc2);
10793 }
10794
10795 LogFlowFunc(("returns %Rrc\n", rc));
10796 return rc;
10797}
10798
10799
10800VBOXDDU_DECL(int) VDAsyncWrite(PVBOXHDD pDisk, uint64_t uOffset, size_t cbWrite,
10801 PCRTSGBUF pcSgBuf,
10802 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
10803 void *pvUser1, void *pvUser2)
10804{
10805 int rc;
10806 int rc2;
10807 bool fLockWrite = false;
10808 PVDIOCTX pIoCtx = NULL;
10809
10810 LogFlowFunc(("pDisk=%#p uOffset=%llu cSgBuf=%#p cbWrite=%zu pvUser1=%#p pvUser2=%#p\n",
10811 pDisk, uOffset, pcSgBuf, cbWrite, pvUser1, pvUser2));
10812 do
10813 {
10814 /* sanity check */
10815 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10816 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10817
10818 /* Check arguments. */
10819 AssertMsgBreakStmt(cbWrite,
10820 ("cbWrite=%zu\n", cbWrite),
10821 rc = VERR_INVALID_PARAMETER);
10822 AssertMsgBreakStmt(VALID_PTR(pcSgBuf),
10823 ("pcSgBuf=%#p\n", pcSgBuf),
10824 rc = VERR_INVALID_PARAMETER);
10825
10826 rc2 = vdThreadStartWrite(pDisk);
10827 AssertRC(rc2);
10828 fLockWrite = true;
10829
10830 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
10831 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
10832 uOffset, cbWrite, pDisk->cbSize),
10833 rc = VERR_INVALID_PARAMETER);
10834 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
10835
10836 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_WRITE, uOffset,
10837 cbWrite, pDisk->pLast, pcSgBuf,
10838 pfnComplete, pvUser1, pvUser2,
10839 NULL, vdWriteHelperAsync,
10840 VDIOCTX_FLAGS_DEFAULT);
10841 if (!pIoCtx)
10842 {
10843 rc = VERR_NO_MEMORY;
10844 break;
10845 }
10846
10847 rc = vdIoCtxProcessTryLockDefer(pIoCtx);
10848 if (rc == VINF_VD_ASYNC_IO_FINISHED)
10849 {
10850 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
10851 vdIoCtxFree(pDisk, pIoCtx);
10852 else
10853 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
10854 }
10855 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
10856 vdIoCtxFree(pDisk, pIoCtx);
10857 } while (0);
10858
10859 if (RT_UNLIKELY(fLockWrite) && ( rc == VINF_VD_ASYNC_IO_FINISHED
10860 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
10861 {
10862 rc2 = vdThreadFinishWrite(pDisk);
10863 AssertRC(rc2);
10864 }
10865
10866 LogFlowFunc(("returns %Rrc\n", rc));
10867 return rc;
10868}
10869
10870
10871VBOXDDU_DECL(int) VDAsyncFlush(PVBOXHDD pDisk, PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
10872 void *pvUser1, void *pvUser2)
10873{
10874 int rc;
10875 int rc2;
10876 bool fLockWrite = false;
10877 PVDIOCTX pIoCtx = NULL;
10878
10879 LogFlowFunc(("pDisk=%#p\n", pDisk));
10880
10881 do
10882 {
10883 /* sanity check */
10884 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10885 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10886
10887 rc2 = vdThreadStartWrite(pDisk);
10888 AssertRC(rc2);
10889 fLockWrite = true;
10890
10891 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
10892
10893 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_FLUSH, 0,
10894 0, pDisk->pLast, NULL,
10895 pfnComplete, pvUser1, pvUser2,
10896 NULL, vdFlushHelperAsync,
10897 VDIOCTX_FLAGS_DEFAULT);
10898 if (!pIoCtx)
10899 {
10900 rc = VERR_NO_MEMORY;
10901 break;
10902 }
10903
10904 rc = vdIoCtxProcessTryLockDefer(pIoCtx);
10905 if (rc == VINF_VD_ASYNC_IO_FINISHED)
10906 {
10907 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
10908 vdIoCtxFree(pDisk, pIoCtx);
10909 else
10910 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
10911 }
10912 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
10913 vdIoCtxFree(pDisk, pIoCtx);
10914 } while (0);
10915
10916 if (RT_UNLIKELY(fLockWrite) && ( rc == VINF_VD_ASYNC_IO_FINISHED
10917 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
10918 {
10919 rc2 = vdThreadFinishWrite(pDisk);
10920 AssertRC(rc2);
10921 }
10922
10923 LogFlowFunc(("returns %Rrc\n", rc));
10924 return rc;
10925}
10926
10927VBOXDDU_DECL(int) VDAsyncDiscardRanges(PVBOXHDD pDisk, PCRTRANGE paRanges, unsigned cRanges,
10928 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
10929 void *pvUser1, void *pvUser2)
10930{
10931 int rc;
10932 int rc2;
10933 bool fLockWrite = false;
10934 PVDIOCTX pIoCtx = NULL;
10935
10936 LogFlowFunc(("pDisk=%#p\n", pDisk));
10937
10938 do
10939 {
10940 /* sanity check */
10941 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10942 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10943
10944 rc2 = vdThreadStartWrite(pDisk);
10945 AssertRC(rc2);
10946 fLockWrite = true;
10947
10948 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
10949
10950 pIoCtx = vdIoCtxDiscardAlloc(pDisk, paRanges, cRanges,
10951 pfnComplete, pvUser1, pvUser2, NULL,
10952 vdDiscardHelperAsync,
10953 VDIOCTX_FLAGS_DEFAULT);
10954 if (!pIoCtx)
10955 {
10956 rc = VERR_NO_MEMORY;
10957 break;
10958 }
10959
10960 rc = vdIoCtxProcessTryLockDefer(pIoCtx);
10961 if (rc == VINF_VD_ASYNC_IO_FINISHED)
10962 {
10963 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
10964 vdIoCtxFree(pDisk, pIoCtx);
10965 else
10966 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
10967 }
10968 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
10969 vdIoCtxFree(pDisk, pIoCtx);
10970 } while (0);
10971
10972 if (RT_UNLIKELY(fLockWrite) && ( rc == VINF_VD_ASYNC_IO_FINISHED
10973 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
10974 {
10975 rc2 = vdThreadFinishWrite(pDisk);
10976 AssertRC(rc2);
10977 }
10978
10979 LogFlowFunc(("returns %Rrc\n", rc));
10980 return rc;
10981}
10982
10983VBOXDDU_DECL(int) VDRepair(PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
10984 const char *pszFilename, const char *pszBackend,
10985 uint32_t fFlags)
10986{
10987 int rc = VERR_NOT_SUPPORTED;
10988 PCVBOXHDDBACKEND pBackend = NULL;
10989 VDINTERFACEIOINT VDIfIoInt;
10990 VDINTERFACEIO VDIfIoFallback;
10991 PVDINTERFACEIO pInterfaceIo;
10992
10993 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
10994 /* Check arguments. */
10995 AssertMsgReturn(VALID_PTR(pszFilename) && *pszFilename,
10996 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
10997 VERR_INVALID_PARAMETER);
10998 AssertMsgReturn(VALID_PTR(pszBackend),
10999 ("pszBackend=%#p\n", pszBackend),
11000 VERR_INVALID_PARAMETER);
11001 AssertMsgReturn((fFlags & ~VD_REPAIR_FLAGS_MASK) == 0,
11002 ("fFlags=%#x\n", fFlags),
11003 VERR_INVALID_PARAMETER);
11004
11005 pInterfaceIo = VDIfIoGet(pVDIfsImage);
11006 if (!pInterfaceIo)
11007 {
11008 /*
11009 * Caller doesn't provide an I/O interface, create our own using the
11010 * native file API.
11011 */
11012 vdIfIoFallbackCallbacksSetup(&VDIfIoFallback);
11013 pInterfaceIo = &VDIfIoFallback;
11014 }
11015
11016 /* Set up the internal I/O interface. */
11017 AssertReturn(!VDIfIoIntGet(pVDIfsImage), VERR_INVALID_PARAMETER);
11018 VDIfIoInt.pfnOpen = vdIOIntOpenLimited;
11019 VDIfIoInt.pfnClose = vdIOIntCloseLimited;
11020 VDIfIoInt.pfnDelete = vdIOIntDeleteLimited;
11021 VDIfIoInt.pfnMove = vdIOIntMoveLimited;
11022 VDIfIoInt.pfnGetFreeSpace = vdIOIntGetFreeSpaceLimited;
11023 VDIfIoInt.pfnGetModificationTime = vdIOIntGetModificationTimeLimited;
11024 VDIfIoInt.pfnGetSize = vdIOIntGetSizeLimited;
11025 VDIfIoInt.pfnSetSize = vdIOIntSetSizeLimited;
11026 VDIfIoInt.pfnReadUser = vdIOIntReadUserLimited;
11027 VDIfIoInt.pfnWriteUser = vdIOIntWriteUserLimited;
11028 VDIfIoInt.pfnReadMeta = vdIOIntReadMetaLimited;
11029 VDIfIoInt.pfnWriteMeta = vdIOIntWriteMetaLimited;
11030 VDIfIoInt.pfnFlush = vdIOIntFlushLimited;
11031 rc = VDInterfaceAdd(&VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
11032 pInterfaceIo, sizeof(VDINTERFACEIOINT), &pVDIfsImage);
11033 AssertRC(rc);
11034
11035 rc = vdFindBackend(pszBackend, &pBackend);
11036 if (RT_SUCCESS(rc))
11037 {
11038 if (pBackend->pfnRepair)
11039 rc = pBackend->pfnRepair(pszFilename, pVDIfsDisk, pVDIfsImage, fFlags);
11040 else
11041 rc = VERR_VD_IMAGE_REPAIR_NOT_SUPPORTED;
11042 }
11043
11044 LogFlowFunc(("returns %Rrc\n", rc));
11045 return rc;
11046}
11047
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