VirtualBox

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

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

Storage/VD: Add methods to load a plugin from a given path/file. Preparation for support of VD plugins in extension packs

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