VirtualBox

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

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

Storage: Log & Assertion formatting fixes.

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