VirtualBox

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

Last change on this file since 51750 was 51750, checked in by vboxsync, 11 years ago

Storage: Fix concurrency issue with filters configured which can result in corrupt data being transfered, the filters must be applied under the lock to avoid multiple threads accessing the same filter at the same time

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette