VirtualBox

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

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

VD: Add API to query information about a filter

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