VirtualBox

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

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

Storage/VD: Cleanup VD plugin handling. One shared object can now support an arbitrary number of image backends instead of just one like before

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

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