VirtualBox

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

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

Storage/VD: Fix crash

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 365.4 KB
Line 
1/* $Id: VD.cpp 51756 2014-06-29 20:31:36Z 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 * Allocates and initializes an empty HDD container.
5549 * No image files are opened.
5550 *
5551 * @returns VBox status code.
5552 * @param pVDIfsDisk Pointer to the per-disk VD interface list.
5553 * @param enmType Type of the image container.
5554 * @param ppDisk Where to store the reference to HDD container.
5555 */
5556VBOXDDU_DECL(int) VDCreate(PVDINTERFACE pVDIfsDisk, VDTYPE enmType, PVBOXHDD *ppDisk)
5557{
5558 int rc = VINF_SUCCESS;
5559 PVBOXHDD pDisk = NULL;
5560
5561 LogFlowFunc(("pVDIfsDisk=%#p\n", pVDIfsDisk));
5562 do
5563 {
5564 /* Check arguments. */
5565 AssertMsgBreakStmt(VALID_PTR(ppDisk),
5566 ("ppDisk=%#p\n", ppDisk),
5567 rc = VERR_INVALID_PARAMETER);
5568
5569 pDisk = (PVBOXHDD)RTMemAllocZ(sizeof(VBOXHDD));
5570 if (pDisk)
5571 {
5572 pDisk->u32Signature = VBOXHDDDISK_SIGNATURE;
5573 pDisk->enmType = enmType;
5574 pDisk->cImages = 0;
5575 pDisk->pBase = NULL;
5576 pDisk->pLast = NULL;
5577 pDisk->cbSize = 0;
5578 pDisk->PCHSGeometry.cCylinders = 0;
5579 pDisk->PCHSGeometry.cHeads = 0;
5580 pDisk->PCHSGeometry.cSectors = 0;
5581 pDisk->LCHSGeometry.cCylinders = 0;
5582 pDisk->LCHSGeometry.cHeads = 0;
5583 pDisk->LCHSGeometry.cSectors = 0;
5584 pDisk->pVDIfsDisk = pVDIfsDisk;
5585 pDisk->pInterfaceError = NULL;
5586 pDisk->pInterfaceThreadSync = NULL;
5587 pDisk->pIoCtxLockOwner = NULL;
5588 pDisk->pIoCtxHead = NULL;
5589 pDisk->fLocked = false;
5590 pDisk->hMemCacheIoCtx = NIL_RTMEMCACHE;
5591 pDisk->hMemCacheIoTask = NIL_RTMEMCACHE;
5592 pDisk->pFilterHead = NULL;
5593 pDisk->pFilterTail = NULL;
5594
5595 /* Create the I/O ctx cache */
5596 rc = RTMemCacheCreate(&pDisk->hMemCacheIoCtx, sizeof(VDIOCTX), 0, UINT32_MAX,
5597 NULL, NULL, NULL, 0);
5598 if (RT_FAILURE(rc))
5599 break;
5600
5601 /* Create the I/O task cache */
5602 rc = RTMemCacheCreate(&pDisk->hMemCacheIoTask, sizeof(VDIOTASK), 0, UINT32_MAX,
5603 NULL, NULL, NULL, 0);
5604 if (RT_FAILURE(rc))
5605 break;
5606
5607 pDisk->pInterfaceError = VDIfErrorGet(pVDIfsDisk);
5608 pDisk->pInterfaceThreadSync = VDIfThreadSyncGet(pVDIfsDisk);
5609
5610 *ppDisk = pDisk;
5611 }
5612 else
5613 {
5614 rc = VERR_NO_MEMORY;
5615 break;
5616 }
5617 } while (0);
5618
5619 if ( RT_FAILURE(rc)
5620 && pDisk)
5621 {
5622 if (pDisk->hMemCacheIoCtx != NIL_RTMEMCACHE)
5623 RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
5624 if (pDisk->hMemCacheIoTask != NIL_RTMEMCACHE)
5625 RTMemCacheDestroy(pDisk->hMemCacheIoTask);
5626 }
5627
5628 LogFlowFunc(("returns %Rrc (pDisk=%#p)\n", rc, pDisk));
5629 return rc;
5630}
5631
5632/**
5633 * Destroys HDD container.
5634 * If container has opened image files they will be closed.
5635 *
5636 * @returns VBox status code.
5637 * @param pDisk Pointer to HDD container.
5638 */
5639VBOXDDU_DECL(int) VDDestroy(PVBOXHDD pDisk)
5640{
5641 int rc = VINF_SUCCESS;
5642 LogFlowFunc(("pDisk=%#p\n", pDisk));
5643 do
5644 {
5645 /* sanity check */
5646 AssertPtrBreak(pDisk);
5647 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5648 Assert(!pDisk->fLocked);
5649
5650 rc = VDCloseAll(pDisk);
5651 int rc2 = VDFilterRemoveAll(pDisk);
5652 if (RT_SUCCESS(rc))
5653 rc = rc2;
5654
5655 RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
5656 RTMemCacheDestroy(pDisk->hMemCacheIoTask);
5657 RTMemFree(pDisk);
5658 } while (0);
5659 LogFlowFunc(("returns %Rrc\n", rc));
5660 return rc;
5661}
5662
5663/**
5664 * Try to get the backend name which can use this image.
5665 *
5666 * @returns VBox status code.
5667 * VINF_SUCCESS if a plugin was found.
5668 * ppszFormat contains the string which can be used as backend name.
5669 * VERR_NOT_SUPPORTED if no backend was found.
5670 * @param pVDIfsDisk Pointer to the per-disk VD interface list.
5671 * @param pVDIfsImage Pointer to the per-image VD interface list.
5672 * @param pszFilename Name of the image file for which the backend is queried.
5673 * @param ppszFormat Receives pointer of the UTF-8 string which contains the format name.
5674 * The returned pointer must be freed using RTStrFree().
5675 */
5676VBOXDDU_DECL(int) VDGetFormat(PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
5677 const char *pszFilename, char **ppszFormat, VDTYPE *penmType)
5678{
5679 int rc = VERR_NOT_SUPPORTED;
5680 VDINTERFACEIOINT VDIfIoInt;
5681 VDINTERFACEIO VDIfIoFallback;
5682 PVDINTERFACEIO pInterfaceIo;
5683
5684 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
5685 /* Check arguments. */
5686 AssertMsgReturn(VALID_PTR(pszFilename) && *pszFilename,
5687 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
5688 VERR_INVALID_PARAMETER);
5689 AssertMsgReturn(VALID_PTR(ppszFormat),
5690 ("ppszFormat=%#p\n", ppszFormat),
5691 VERR_INVALID_PARAMETER);
5692 AssertMsgReturn(VALID_PTR(penmType),
5693 ("penmType=%#p\n", penmType),
5694 VERR_INVALID_PARAMETER);
5695
5696 if (!g_apBackends)
5697 VDInit();
5698
5699 pInterfaceIo = VDIfIoGet(pVDIfsImage);
5700 if (!pInterfaceIo)
5701 {
5702 /*
5703 * Caller doesn't provide an I/O interface, create our own using the
5704 * native file API.
5705 */
5706 vdIfIoFallbackCallbacksSetup(&VDIfIoFallback);
5707 pInterfaceIo = &VDIfIoFallback;
5708 }
5709
5710 /* Set up the internal I/O interface. */
5711 AssertReturn(!VDIfIoIntGet(pVDIfsImage), VERR_INVALID_PARAMETER);
5712 VDIfIoInt.pfnOpen = vdIOIntOpenLimited;
5713 VDIfIoInt.pfnClose = vdIOIntCloseLimited;
5714 VDIfIoInt.pfnDelete = vdIOIntDeleteLimited;
5715 VDIfIoInt.pfnMove = vdIOIntMoveLimited;
5716 VDIfIoInt.pfnGetFreeSpace = vdIOIntGetFreeSpaceLimited;
5717 VDIfIoInt.pfnGetModificationTime = vdIOIntGetModificationTimeLimited;
5718 VDIfIoInt.pfnGetSize = vdIOIntGetSizeLimited;
5719 VDIfIoInt.pfnSetSize = vdIOIntSetSizeLimited;
5720 VDIfIoInt.pfnReadUser = vdIOIntReadUserLimited;
5721 VDIfIoInt.pfnWriteUser = vdIOIntWriteUserLimited;
5722 VDIfIoInt.pfnReadMeta = vdIOIntReadMetaLimited;
5723 VDIfIoInt.pfnWriteMeta = vdIOIntWriteMetaLimited;
5724 VDIfIoInt.pfnFlush = vdIOIntFlushLimited;
5725 rc = VDInterfaceAdd(&VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
5726 pInterfaceIo, sizeof(VDINTERFACEIOINT), &pVDIfsImage);
5727 AssertRC(rc);
5728
5729 /* Find the backend supporting this file format. */
5730 for (unsigned i = 0; i < g_cBackends; i++)
5731 {
5732 if (g_apBackends[i]->pfnCheckIfValid)
5733 {
5734 rc = g_apBackends[i]->pfnCheckIfValid(pszFilename, pVDIfsDisk,
5735 pVDIfsImage, penmType);
5736 if ( RT_SUCCESS(rc)
5737 /* The correct backend has been found, but there is a small
5738 * incompatibility so that the file cannot be used. Stop here
5739 * and signal success - the actual open will of course fail,
5740 * but that will create a really sensible error message. */
5741 || ( rc != VERR_VD_GEN_INVALID_HEADER
5742 && rc != VERR_VD_VDI_INVALID_HEADER
5743 && rc != VERR_VD_VMDK_INVALID_HEADER
5744 && rc != VERR_VD_ISCSI_INVALID_HEADER
5745 && rc != VERR_VD_VHD_INVALID_HEADER
5746 && rc != VERR_VD_RAW_INVALID_HEADER
5747 && rc != VERR_VD_PARALLELS_INVALID_HEADER
5748 && rc != VERR_VD_DMG_INVALID_HEADER))
5749 {
5750 /* Copy the name into the new string. */
5751 char *pszFormat = RTStrDup(g_apBackends[i]->pszBackendName);
5752 if (!pszFormat)
5753 {
5754 rc = VERR_NO_MEMORY;
5755 break;
5756 }
5757 *ppszFormat = pszFormat;
5758 /* Do not consider the typical file access errors as success,
5759 * which allows the caller to deal with such issues. */
5760 if ( rc != VERR_ACCESS_DENIED
5761 && rc != VERR_PATH_NOT_FOUND
5762 && rc != VERR_FILE_NOT_FOUND)
5763 rc = VINF_SUCCESS;
5764 break;
5765 }
5766 rc = VERR_NOT_SUPPORTED;
5767 }
5768 }
5769
5770 /* Try the cache backends. */
5771 if (rc == VERR_NOT_SUPPORTED)
5772 {
5773 for (unsigned i = 0; i < g_cCacheBackends; i++)
5774 {
5775 if (g_apCacheBackends[i]->pfnProbe)
5776 {
5777 rc = g_apCacheBackends[i]->pfnProbe(pszFilename, pVDIfsDisk,
5778 pVDIfsImage);
5779 if ( RT_SUCCESS(rc)
5780 || (rc != VERR_VD_GEN_INVALID_HEADER))
5781 {
5782 /* Copy the name into the new string. */
5783 char *pszFormat = RTStrDup(g_apBackends[i]->pszBackendName);
5784 if (!pszFormat)
5785 {
5786 rc = VERR_NO_MEMORY;
5787 break;
5788 }
5789 *ppszFormat = pszFormat;
5790 rc = VINF_SUCCESS;
5791 break;
5792 }
5793 rc = VERR_NOT_SUPPORTED;
5794 }
5795 }
5796 }
5797
5798 LogFlowFunc(("returns %Rrc *ppszFormat=\"%s\"\n", rc, *ppszFormat));
5799 return rc;
5800}
5801
5802/**
5803 * Opens an image file.
5804 *
5805 * The first opened image file in HDD container must have a base image type,
5806 * others (next opened images) must be a differencing or undo images.
5807 * Linkage is checked for differencing image to be in consistence with the previously opened image.
5808 * When another differencing image is opened and the last image was opened in read/write access
5809 * mode, then the last image is reopened in read-only with deny write sharing mode. This allows
5810 * other processes to use images in read-only mode too.
5811 *
5812 * Note that the image is opened in read-only mode if a read/write open is not possible.
5813 * Use VDIsReadOnly to check open mode.
5814 *
5815 * @returns VBox status code.
5816 * @param pDisk Pointer to HDD container.
5817 * @param pszBackend Name of the image file backend to use.
5818 * @param pszFilename Name of the image file to open.
5819 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
5820 * @param pVDIfsImage Pointer to the per-image VD interface list.
5821 */
5822VBOXDDU_DECL(int) VDOpen(PVBOXHDD pDisk, const char *pszBackend,
5823 const char *pszFilename, unsigned uOpenFlags,
5824 PVDINTERFACE pVDIfsImage)
5825{
5826 int rc = VINF_SUCCESS;
5827 int rc2;
5828 bool fLockWrite = false;
5829 PVDIMAGE pImage = NULL;
5830
5831 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x, pVDIfsImage=%#p\n",
5832 pDisk, pszBackend, pszFilename, uOpenFlags, pVDIfsImage));
5833
5834 do
5835 {
5836 /* sanity check */
5837 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
5838 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5839
5840 /* Check arguments. */
5841 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
5842 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
5843 rc = VERR_INVALID_PARAMETER);
5844 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
5845 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
5846 rc = VERR_INVALID_PARAMETER);
5847 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
5848 ("uOpenFlags=%#x\n", uOpenFlags),
5849 rc = VERR_INVALID_PARAMETER);
5850 AssertMsgBreakStmt( !(uOpenFlags & VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)
5851 || (uOpenFlags & VD_OPEN_FLAGS_READONLY),
5852 ("uOpenFlags=%#x\n", uOpenFlags),
5853 rc = VERR_INVALID_PARAMETER);
5854
5855 /*
5856 * Destroy the current discard state first which might still have pending blocks
5857 * for the currently opened image which will be switched to readonly mode.
5858 */
5859 /* Lock disk for writing, as we modify pDisk information below. */
5860 rc2 = vdThreadStartWrite(pDisk);
5861 AssertRC(rc2);
5862 fLockWrite = true;
5863 rc = vdDiscardStateDestroy(pDisk);
5864 if (RT_FAILURE(rc))
5865 break;
5866 rc2 = vdThreadFinishWrite(pDisk);
5867 AssertRC(rc2);
5868 fLockWrite = false;
5869
5870 /* Set up image descriptor. */
5871 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
5872 if (!pImage)
5873 {
5874 rc = VERR_NO_MEMORY;
5875 break;
5876 }
5877 pImage->pszFilename = RTStrDup(pszFilename);
5878 if (!pImage->pszFilename)
5879 {
5880 rc = VERR_NO_MEMORY;
5881 break;
5882 }
5883
5884 pImage->VDIo.pDisk = pDisk;
5885 pImage->pVDIfsImage = pVDIfsImage;
5886
5887 rc = vdFindBackend(pszBackend, &pImage->Backend);
5888 if (RT_FAILURE(rc))
5889 break;
5890 if (!pImage->Backend)
5891 {
5892 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
5893 N_("VD: unknown backend name '%s'"), pszBackend);
5894 break;
5895 }
5896
5897 /*
5898 * Fail if the backend can't do async I/O but the
5899 * flag is set.
5900 */
5901 if ( !(pImage->Backend->uBackendCaps & VD_CAP_ASYNC)
5902 && (uOpenFlags & VD_OPEN_FLAGS_ASYNC_IO))
5903 {
5904 rc = vdError(pDisk, VERR_NOT_SUPPORTED, RT_SRC_POS,
5905 N_("VD: Backend '%s' does not support async I/O"), pszBackend);
5906 break;
5907 }
5908
5909 /*
5910 * Fail if the backend doesn't support the discard operation but the
5911 * flag is set.
5912 */
5913 if ( !(pImage->Backend->uBackendCaps & VD_CAP_DISCARD)
5914 && (uOpenFlags & VD_OPEN_FLAGS_DISCARD))
5915 {
5916 rc = vdError(pDisk, VERR_VD_DISCARD_NOT_SUPPORTED, RT_SRC_POS,
5917 N_("VD: Backend '%s' does not support discard"), pszBackend);
5918 break;
5919 }
5920
5921 /* Set up the I/O interface. */
5922 pImage->VDIo.pInterfaceIo = VDIfIoGet(pVDIfsImage);
5923 if (!pImage->VDIo.pInterfaceIo)
5924 {
5925 vdIfIoFallbackCallbacksSetup(&pImage->VDIo.VDIfIo);
5926 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIo.Core, "VD_IO", VDINTERFACETYPE_IO,
5927 pDisk, sizeof(VDINTERFACEIO), &pVDIfsImage);
5928 pImage->VDIo.pInterfaceIo = &pImage->VDIo.VDIfIo;
5929 }
5930
5931 /* Set up the internal I/O interface. */
5932 AssertBreakStmt(!VDIfIoIntGet(pVDIfsImage), rc = VERR_INVALID_PARAMETER);
5933 vdIfIoIntCallbacksSetup(&pImage->VDIo.VDIfIoInt);
5934 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
5935 &pImage->VDIo, sizeof(VDINTERFACEIOINT), &pImage->pVDIfsImage);
5936 AssertRC(rc);
5937
5938 pImage->uOpenFlags = uOpenFlags & (VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_DISCARD | VD_OPEN_FLAGS_IGNORE_FLUSH | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS);
5939 pImage->VDIo.fIgnoreFlush = (uOpenFlags & VD_OPEN_FLAGS_IGNORE_FLUSH) != 0;
5940 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
5941 uOpenFlags & ~(VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_IGNORE_FLUSH | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS),
5942 pDisk->pVDIfsDisk,
5943 pImage->pVDIfsImage,
5944 pDisk->enmType,
5945 &pImage->pBackendData);
5946 /*
5947 * If the image is corrupted and there is a repair method try to repair it
5948 * first if it was openend in read-write mode and open again afterwards.
5949 */
5950 if ( RT_UNLIKELY(rc == VERR_VD_IMAGE_CORRUPTED)
5951 && !(uOpenFlags & VD_OPEN_FLAGS_READONLY)
5952 && pImage->Backend->pfnRepair)
5953 {
5954 rc = pImage->Backend->pfnRepair(pszFilename, pDisk->pVDIfsDisk, pImage->pVDIfsImage, 0 /* fFlags */);
5955 if (RT_SUCCESS(rc))
5956 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
5957 uOpenFlags & ~(VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_IGNORE_FLUSH | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS),
5958 pDisk->pVDIfsDisk,
5959 pImage->pVDIfsImage,
5960 pDisk->enmType,
5961 &pImage->pBackendData);
5962 else
5963 {
5964 rc = vdError(pDisk, rc, RT_SRC_POS,
5965 N_("VD: error %Rrc repairing corrupted image file '%s'"), rc, pszFilename);
5966 break;
5967 }
5968 }
5969 else if (RT_UNLIKELY(rc == VERR_VD_IMAGE_CORRUPTED))
5970 {
5971 rc = vdError(pDisk, rc, RT_SRC_POS,
5972 N_("VD: Image file '%s' is corrupted and can't be opened"), pszFilename);
5973 break;
5974 }
5975
5976 /* If the open in read-write mode failed, retry in read-only mode. */
5977 if (RT_FAILURE(rc))
5978 {
5979 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
5980 && ( rc == VERR_ACCESS_DENIED
5981 || rc == VERR_PERMISSION_DENIED
5982 || rc == VERR_WRITE_PROTECT
5983 || rc == VERR_SHARING_VIOLATION
5984 || rc == VERR_FILE_LOCK_FAILED))
5985 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
5986 (uOpenFlags & ~(VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS))
5987 | VD_OPEN_FLAGS_READONLY,
5988 pDisk->pVDIfsDisk,
5989 pImage->pVDIfsImage,
5990 pDisk->enmType,
5991 &pImage->pBackendData);
5992 if (RT_FAILURE(rc))
5993 {
5994 rc = vdError(pDisk, rc, RT_SRC_POS,
5995 N_("VD: error %Rrc opening image file '%s'"), rc, pszFilename);
5996 break;
5997 }
5998 }
5999
6000 /* Lock disk for writing, as we modify pDisk information below. */
6001 rc2 = vdThreadStartWrite(pDisk);
6002 AssertRC(rc2);
6003 fLockWrite = true;
6004
6005 pImage->VDIo.pBackendData = pImage->pBackendData;
6006
6007 /* Check image type. As the image itself has only partial knowledge
6008 * whether it's a base image or not, this info is derived here. The
6009 * base image can be fixed or normal, all others must be normal or
6010 * diff images. Some image formats don't distinguish between normal
6011 * and diff images, so this must be corrected here. */
6012 unsigned uImageFlags;
6013 uImageFlags = pImage->Backend->pfnGetImageFlags(pImage->pBackendData);
6014 if (RT_FAILURE(rc))
6015 uImageFlags = VD_IMAGE_FLAGS_NONE;
6016 if ( RT_SUCCESS(rc)
6017 && !(uOpenFlags & VD_OPEN_FLAGS_INFO))
6018 {
6019 if ( pDisk->cImages == 0
6020 && (uImageFlags & VD_IMAGE_FLAGS_DIFF))
6021 {
6022 rc = VERR_VD_INVALID_TYPE;
6023 break;
6024 }
6025 else if (pDisk->cImages != 0)
6026 {
6027 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
6028 {
6029 rc = VERR_VD_INVALID_TYPE;
6030 break;
6031 }
6032 else
6033 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
6034 }
6035 }
6036
6037 /* Ensure we always get correct diff information, even if the backend
6038 * doesn't actually have a stored flag for this. It must not return
6039 * bogus information for the parent UUID if it is not a diff image. */
6040 RTUUID parentUuid;
6041 RTUuidClear(&parentUuid);
6042 rc2 = pImage->Backend->pfnGetParentUuid(pImage->pBackendData, &parentUuid);
6043 if (RT_SUCCESS(rc2) && !RTUuidIsNull(&parentUuid))
6044 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
6045
6046 pImage->uImageFlags = uImageFlags;
6047
6048 /* Force sane optimization settings. It's not worth avoiding writes
6049 * to fixed size images. The overhead would have almost no payback. */
6050 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
6051 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
6052
6053 /** @todo optionally check UUIDs */
6054
6055 /* Cache disk information. */
6056 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
6057
6058 /* Cache PCHS geometry. */
6059 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
6060 &pDisk->PCHSGeometry);
6061 if (RT_FAILURE(rc2))
6062 {
6063 pDisk->PCHSGeometry.cCylinders = 0;
6064 pDisk->PCHSGeometry.cHeads = 0;
6065 pDisk->PCHSGeometry.cSectors = 0;
6066 }
6067 else
6068 {
6069 /* Make sure the PCHS geometry is properly clipped. */
6070 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
6071 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
6072 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
6073 }
6074
6075 /* Cache LCHS geometry. */
6076 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
6077 &pDisk->LCHSGeometry);
6078 if (RT_FAILURE(rc2))
6079 {
6080 pDisk->LCHSGeometry.cCylinders = 0;
6081 pDisk->LCHSGeometry.cHeads = 0;
6082 pDisk->LCHSGeometry.cSectors = 0;
6083 }
6084 else
6085 {
6086 /* Make sure the LCHS geometry is properly clipped. */
6087 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
6088 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
6089 }
6090
6091 if (pDisk->cImages != 0)
6092 {
6093 /* Switch previous image to read-only mode. */
6094 unsigned uOpenFlagsPrevImg;
6095 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
6096 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
6097 {
6098 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
6099 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pBackendData, uOpenFlagsPrevImg);
6100 }
6101 }
6102
6103 if (RT_SUCCESS(rc))
6104 {
6105 /* Image successfully opened, make it the last image. */
6106 vdAddImageToList(pDisk, pImage);
6107 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
6108 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
6109 }
6110 else
6111 {
6112 /* Error detected, but image opened. Close image. */
6113 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, false);
6114 AssertRC(rc2);
6115 pImage->pBackendData = NULL;
6116 }
6117 } while (0);
6118
6119 if (RT_UNLIKELY(fLockWrite))
6120 {
6121 rc2 = vdThreadFinishWrite(pDisk);
6122 AssertRC(rc2);
6123 }
6124
6125 if (RT_FAILURE(rc))
6126 {
6127 if (pImage)
6128 {
6129 if (pImage->pszFilename)
6130 RTStrFree(pImage->pszFilename);
6131 RTMemFree(pImage);
6132 }
6133 }
6134
6135 LogFlowFunc(("returns %Rrc\n", rc));
6136 return rc;
6137}
6138
6139/**
6140 * Opens a cache image.
6141 *
6142 * @return VBox status code.
6143 * @param pDisk Pointer to the HDD container which should use the cache image.
6144 * @param pszBackend Name of the cache file backend to use (case insensitive).
6145 * @param pszFilename Name of the cache image to open.
6146 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
6147 * @param pVDIfsCache Pointer to the per-cache VD interface list.
6148 */
6149VBOXDDU_DECL(int) VDCacheOpen(PVBOXHDD pDisk, const char *pszBackend,
6150 const char *pszFilename, unsigned uOpenFlags,
6151 PVDINTERFACE pVDIfsCache)
6152{
6153 int rc = VINF_SUCCESS;
6154 int rc2;
6155 bool fLockWrite = false;
6156 PVDCACHE pCache = NULL;
6157
6158 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x, pVDIfsCache=%#p\n",
6159 pDisk, pszBackend, pszFilename, uOpenFlags, pVDIfsCache));
6160
6161 do
6162 {
6163 /* sanity check */
6164 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6165 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6166
6167 /* Check arguments. */
6168 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
6169 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
6170 rc = VERR_INVALID_PARAMETER);
6171 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
6172 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
6173 rc = VERR_INVALID_PARAMETER);
6174 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
6175 ("uOpenFlags=%#x\n", uOpenFlags),
6176 rc = VERR_INVALID_PARAMETER);
6177
6178 /* Set up image descriptor. */
6179 pCache = (PVDCACHE)RTMemAllocZ(sizeof(VDCACHE));
6180 if (!pCache)
6181 {
6182 rc = VERR_NO_MEMORY;
6183 break;
6184 }
6185 pCache->pszFilename = RTStrDup(pszFilename);
6186 if (!pCache->pszFilename)
6187 {
6188 rc = VERR_NO_MEMORY;
6189 break;
6190 }
6191
6192 pCache->VDIo.pDisk = pDisk;
6193 pCache->pVDIfsCache = pVDIfsCache;
6194
6195 rc = vdFindCacheBackend(pszBackend, &pCache->Backend);
6196 if (RT_FAILURE(rc))
6197 break;
6198 if (!pCache->Backend)
6199 {
6200 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
6201 N_("VD: unknown backend name '%s'"), pszBackend);
6202 break;
6203 }
6204
6205 /* Set up the I/O interface. */
6206 pCache->VDIo.pInterfaceIo = VDIfIoGet(pVDIfsCache);
6207 if (!pCache->VDIo.pInterfaceIo)
6208 {
6209 vdIfIoFallbackCallbacksSetup(&pCache->VDIo.VDIfIo);
6210 rc = VDInterfaceAdd(&pCache->VDIo.VDIfIo.Core, "VD_IO", VDINTERFACETYPE_IO,
6211 pDisk, sizeof(VDINTERFACEIO), &pVDIfsCache);
6212 pCache->VDIo.pInterfaceIo = &pCache->VDIo.VDIfIo;
6213 }
6214
6215 /* Set up the internal I/O interface. */
6216 AssertBreakStmt(!VDIfIoIntGet(pVDIfsCache), rc = VERR_INVALID_PARAMETER);
6217 vdIfIoIntCallbacksSetup(&pCache->VDIo.VDIfIoInt);
6218 rc = VDInterfaceAdd(&pCache->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
6219 &pCache->VDIo, sizeof(VDINTERFACEIOINT), &pCache->pVDIfsCache);
6220 AssertRC(rc);
6221
6222 pCache->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
6223 rc = pCache->Backend->pfnOpen(pCache->pszFilename,
6224 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
6225 pDisk->pVDIfsDisk,
6226 pCache->pVDIfsCache,
6227 &pCache->pBackendData);
6228 /* If the open in read-write mode failed, retry in read-only mode. */
6229 if (RT_FAILURE(rc))
6230 {
6231 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
6232 && ( rc == VERR_ACCESS_DENIED
6233 || rc == VERR_PERMISSION_DENIED
6234 || rc == VERR_WRITE_PROTECT
6235 || rc == VERR_SHARING_VIOLATION
6236 || rc == VERR_FILE_LOCK_FAILED))
6237 rc = pCache->Backend->pfnOpen(pCache->pszFilename,
6238 (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
6239 | VD_OPEN_FLAGS_READONLY,
6240 pDisk->pVDIfsDisk,
6241 pCache->pVDIfsCache,
6242 &pCache->pBackendData);
6243 if (RT_FAILURE(rc))
6244 {
6245 rc = vdError(pDisk, rc, RT_SRC_POS,
6246 N_("VD: error %Rrc opening image file '%s'"), rc, pszFilename);
6247 break;
6248 }
6249 }
6250
6251 /* Lock disk for writing, as we modify pDisk information below. */
6252 rc2 = vdThreadStartWrite(pDisk);
6253 AssertRC(rc2);
6254 fLockWrite = true;
6255
6256 /*
6257 * Check that the modification UUID of the cache and last image
6258 * match. If not the image was modified in-between without the cache.
6259 * The cache might contain stale data.
6260 */
6261 RTUUID UuidImage, UuidCache;
6262
6263 rc = pCache->Backend->pfnGetModificationUuid(pCache->pBackendData,
6264 &UuidCache);
6265 if (RT_SUCCESS(rc))
6266 {
6267 rc = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
6268 &UuidImage);
6269 if (RT_SUCCESS(rc))
6270 {
6271 if (RTUuidCompare(&UuidImage, &UuidCache))
6272 rc = VERR_VD_CACHE_NOT_UP_TO_DATE;
6273 }
6274 }
6275
6276 /*
6277 * We assume that the user knows what he is doing if one of the images
6278 * doesn't support the modification uuid.
6279 */
6280 if (rc == VERR_NOT_SUPPORTED)
6281 rc = VINF_SUCCESS;
6282
6283 if (RT_SUCCESS(rc))
6284 {
6285 /* Cache successfully opened, make it the current one. */
6286 if (!pDisk->pCache)
6287 pDisk->pCache = pCache;
6288 else
6289 rc = VERR_VD_CACHE_ALREADY_EXISTS;
6290 }
6291
6292 if (RT_FAILURE(rc))
6293 {
6294 /* Error detected, but image opened. Close image. */
6295 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, false);
6296 AssertRC(rc2);
6297 pCache->pBackendData = NULL;
6298 }
6299 } while (0);
6300
6301 if (RT_UNLIKELY(fLockWrite))
6302 {
6303 rc2 = vdThreadFinishWrite(pDisk);
6304 AssertRC(rc2);
6305 }
6306
6307 if (RT_FAILURE(rc))
6308 {
6309 if (pCache)
6310 {
6311 if (pCache->pszFilename)
6312 RTStrFree(pCache->pszFilename);
6313 RTMemFree(pCache);
6314 }
6315 }
6316
6317 LogFlowFunc(("returns %Rrc\n", rc));
6318 return rc;
6319}
6320
6321/**
6322 * Adds a filter to the disk.
6323 *
6324 * @returns VBox status code.
6325 * @param pDisk Pointer to the HDD container which should use the filter.
6326 * @param pszFilter Name of the filter backend to use (case insensitive).
6327 * @param pVDIfsFilter Pointer to the per-filter VD interface list.
6328 */
6329VBOXDDU_DECL(int) VDFilterAdd(PVBOXHDD pDisk, const char *pszFilter,
6330 PVDINTERFACE pVDIfsFilter)
6331{
6332 int rc = VINF_SUCCESS;
6333 int rc2;
6334 bool fLockWrite = false;
6335 PVDFILTER pFilter = NULL;
6336
6337 LogFlowFunc(("pDisk=%#p pszFilter=\"%s\" pVDIfsFilter=%#p\n",
6338 pDisk, pszFilter, pVDIfsFilter));
6339
6340 do
6341 {
6342 /* sanity check */
6343 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6344 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6345
6346 /* Check arguments. */
6347 AssertMsgBreakStmt(VALID_PTR(pszFilter) && *pszFilter,
6348 ("pszFilter=%#p \"%s\"\n", pszFilter, pszFilter),
6349 rc = VERR_INVALID_PARAMETER);
6350
6351 /* Set up image descriptor. */
6352 pFilter = (PVDFILTER)RTMemAllocZ(sizeof(VDFILTER));
6353 if (!pFilter)
6354 {
6355 rc = VERR_NO_MEMORY;
6356 break;
6357 }
6358
6359 rc = vdFindFilterBackend(pszFilter, &pFilter->pBackend);
6360 if (RT_FAILURE(rc))
6361 break;
6362 if (!pFilter->pBackend)
6363 {
6364 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
6365 N_("VD: unknown filter backend name '%s'"), pszFilter);
6366 break;
6367 }
6368
6369 pFilter->VDIo.pDisk = pDisk;
6370 pFilter->pVDIfsFilter = pVDIfsFilter;
6371
6372 /* Set up the internal I/O interface. */
6373 AssertBreakStmt(!VDIfIoIntGet(pVDIfsFilter), rc = VERR_INVALID_PARAMETER);
6374 vdIfIoIntCallbacksSetup(&pFilter->VDIo.VDIfIoInt);
6375 rc = VDInterfaceAdd(&pFilter->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
6376 &pFilter->VDIo, sizeof(VDINTERFACEIOINT), &pFilter->pVDIfsFilter);
6377 AssertRC(rc);
6378
6379 rc = pFilter->pBackend->pfnCreate(pDisk->pVDIfsDisk, pFilter->pVDIfsFilter,
6380 &pFilter->pvBackendData);
6381
6382 /* If the open in read-write mode failed, retry in read-only mode. */
6383 if (RT_FAILURE(rc))
6384 {
6385 rc = vdError(pDisk, rc, RT_SRC_POS,
6386 N_("VD: error %Rrc creating filter '%s'"), rc, pszFilter);
6387 break;
6388 }
6389
6390 /* Lock disk for writing, as we modify pDisk information below. */
6391 rc2 = vdThreadStartWrite(pDisk);
6392 AssertRC(rc2);
6393 fLockWrite = true;
6394
6395 /* Add filter to chain. */
6396 vdAddFilterToList(pDisk, pFilter);
6397 } while (0);
6398
6399 if (RT_UNLIKELY(fLockWrite))
6400 {
6401 rc2 = vdThreadFinishWrite(pDisk);
6402 AssertRC(rc2);
6403 }
6404
6405 if (RT_FAILURE(rc))
6406 {
6407 if (pFilter)
6408 RTMemFree(pFilter);
6409 }
6410
6411 LogFlowFunc(("returns %Rrc\n", rc));
6412 return rc;
6413}
6414
6415/**
6416 * Creates and opens a new base image file.
6417 *
6418 * @returns VBox status code.
6419 * @param pDisk Pointer to HDD container.
6420 * @param pszBackend Name of the image file backend to use.
6421 * @param pszFilename Name of the image file to create.
6422 * @param cbSize Image size in bytes.
6423 * @param uImageFlags Flags specifying special image features.
6424 * @param pszComment Pointer to image comment. NULL is ok.
6425 * @param pPCHSGeometry Pointer to physical disk geometry <= (16383,16,63). Not NULL.
6426 * @param pLCHSGeometry Pointer to logical disk geometry <= (x,255,63). Not NULL.
6427 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
6428 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
6429 * @param pVDIfsImage Pointer to the per-image VD interface list.
6430 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
6431 */
6432VBOXDDU_DECL(int) VDCreateBase(PVBOXHDD pDisk, const char *pszBackend,
6433 const char *pszFilename, uint64_t cbSize,
6434 unsigned uImageFlags, const char *pszComment,
6435 PCVDGEOMETRY pPCHSGeometry,
6436 PCVDGEOMETRY pLCHSGeometry,
6437 PCRTUUID pUuid, unsigned uOpenFlags,
6438 PVDINTERFACE pVDIfsImage,
6439 PVDINTERFACE pVDIfsOperation)
6440{
6441 int rc = VINF_SUCCESS;
6442 int rc2;
6443 bool fLockWrite = false, fLockRead = false;
6444 PVDIMAGE pImage = NULL;
6445 RTUUID uuid;
6446
6447 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",
6448 pDisk, pszBackend, pszFilename, cbSize, uImageFlags, pszComment,
6449 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
6450 pPCHSGeometry->cSectors, pLCHSGeometry->cCylinders,
6451 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors, pUuid,
6452 uOpenFlags, pVDIfsImage, pVDIfsOperation));
6453
6454 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
6455
6456 do
6457 {
6458 /* sanity check */
6459 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6460 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6461
6462 /* Check arguments. */
6463 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
6464 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
6465 rc = VERR_INVALID_PARAMETER);
6466 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
6467 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
6468 rc = VERR_INVALID_PARAMETER);
6469 AssertMsgBreakStmt(cbSize,
6470 ("cbSize=%llu\n", cbSize),
6471 rc = VERR_INVALID_PARAMETER);
6472 AssertMsgBreakStmt( ((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0)
6473 || ((uImageFlags & (VD_IMAGE_FLAGS_FIXED | VD_IMAGE_FLAGS_DIFF)) != VD_IMAGE_FLAGS_FIXED),
6474 ("uImageFlags=%#x\n", uImageFlags),
6475 rc = VERR_INVALID_PARAMETER);
6476 /* The PCHS geometry fields may be 0 to leave it for later. */
6477 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
6478 && pPCHSGeometry->cHeads <= 16
6479 && pPCHSGeometry->cSectors <= 63,
6480 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
6481 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
6482 pPCHSGeometry->cSectors),
6483 rc = VERR_INVALID_PARAMETER);
6484 /* The LCHS geometry fields may be 0 to leave it to later autodetection. */
6485 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
6486 && pLCHSGeometry->cHeads <= 255
6487 && pLCHSGeometry->cSectors <= 63,
6488 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
6489 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
6490 pLCHSGeometry->cSectors),
6491 rc = VERR_INVALID_PARAMETER);
6492 /* The UUID may be NULL. */
6493 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
6494 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
6495 rc = VERR_INVALID_PARAMETER);
6496 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
6497 ("uOpenFlags=%#x\n", uOpenFlags),
6498 rc = VERR_INVALID_PARAMETER);
6499
6500 /* Check state. Needs a temporary read lock. Holding the write lock
6501 * all the time would be blocking other activities for too long. */
6502 rc2 = vdThreadStartRead(pDisk);
6503 AssertRC(rc2);
6504 fLockRead = true;
6505 AssertMsgBreakStmt(pDisk->cImages == 0,
6506 ("Create base image cannot be done with other images open\n"),
6507 rc = VERR_VD_INVALID_STATE);
6508 rc2 = vdThreadFinishRead(pDisk);
6509 AssertRC(rc2);
6510 fLockRead = false;
6511
6512 /* Set up image descriptor. */
6513 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
6514 if (!pImage)
6515 {
6516 rc = VERR_NO_MEMORY;
6517 break;
6518 }
6519 pImage->pszFilename = RTStrDup(pszFilename);
6520 if (!pImage->pszFilename)
6521 {
6522 rc = VERR_NO_MEMORY;
6523 break;
6524 }
6525 pImage->VDIo.pDisk = pDisk;
6526 pImage->pVDIfsImage = pVDIfsImage;
6527
6528 /* Set up the I/O interface. */
6529 pImage->VDIo.pInterfaceIo = VDIfIoGet(pVDIfsImage);
6530 if (!pImage->VDIo.pInterfaceIo)
6531 {
6532 vdIfIoFallbackCallbacksSetup(&pImage->VDIo.VDIfIo);
6533 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIo.Core, "VD_IO", VDINTERFACETYPE_IO,
6534 pDisk, sizeof(VDINTERFACEIO), &pVDIfsImage);
6535 pImage->VDIo.pInterfaceIo = &pImage->VDIo.VDIfIo;
6536 }
6537
6538 /* Set up the internal I/O interface. */
6539 AssertBreakStmt(!VDIfIoIntGet(pVDIfsImage), rc = VERR_INVALID_PARAMETER);
6540 vdIfIoIntCallbacksSetup(&pImage->VDIo.VDIfIoInt);
6541 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
6542 &pImage->VDIo, sizeof(VDINTERFACEIOINT), &pImage->pVDIfsImage);
6543 AssertRC(rc);
6544
6545 rc = vdFindBackend(pszBackend, &pImage->Backend);
6546 if (RT_FAILURE(rc))
6547 break;
6548 if (!pImage->Backend)
6549 {
6550 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
6551 N_("VD: unknown backend name '%s'"), pszBackend);
6552 break;
6553 }
6554 if (!(pImage->Backend->uBackendCaps & ( VD_CAP_CREATE_FIXED
6555 | VD_CAP_CREATE_DYNAMIC)))
6556 {
6557 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
6558 N_("VD: backend '%s' cannot create base images"), pszBackend);
6559 break;
6560 }
6561
6562 /* Create UUID if the caller didn't specify one. */
6563 if (!pUuid)
6564 {
6565 rc = RTUuidCreate(&uuid);
6566 if (RT_FAILURE(rc))
6567 {
6568 rc = vdError(pDisk, rc, RT_SRC_POS,
6569 N_("VD: cannot generate UUID for image '%s'"),
6570 pszFilename);
6571 break;
6572 }
6573 pUuid = &uuid;
6574 }
6575
6576 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
6577 uImageFlags &= ~VD_IMAGE_FLAGS_DIFF;
6578 pImage->VDIo.fIgnoreFlush = (uOpenFlags & VD_OPEN_FLAGS_IGNORE_FLUSH) != 0;
6579 rc = pImage->Backend->pfnCreate(pImage->pszFilename, cbSize,
6580 uImageFlags, pszComment, pPCHSGeometry,
6581 pLCHSGeometry, pUuid,
6582 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
6583 0, 99,
6584 pDisk->pVDIfsDisk,
6585 pImage->pVDIfsImage,
6586 pVDIfsOperation,
6587 &pImage->pBackendData);
6588
6589 if (RT_SUCCESS(rc))
6590 {
6591 pImage->VDIo.pBackendData = pImage->pBackendData;
6592 pImage->uImageFlags = uImageFlags;
6593
6594 /* Force sane optimization settings. It's not worth avoiding writes
6595 * to fixed size images. The overhead would have almost no payback. */
6596 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
6597 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
6598
6599 /* Lock disk for writing, as we modify pDisk information below. */
6600 rc2 = vdThreadStartWrite(pDisk);
6601 AssertRC(rc2);
6602 fLockWrite = true;
6603
6604 /** @todo optionally check UUIDs */
6605
6606 /* Re-check state, as the lock wasn't held and another image
6607 * creation call could have been done by another thread. */
6608 AssertMsgStmt(pDisk->cImages == 0,
6609 ("Create base image cannot be done with other images open\n"),
6610 rc = VERR_VD_INVALID_STATE);
6611 }
6612
6613 if (RT_SUCCESS(rc))
6614 {
6615 /* Cache disk information. */
6616 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
6617
6618 /* Cache PCHS geometry. */
6619 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
6620 &pDisk->PCHSGeometry);
6621 if (RT_FAILURE(rc2))
6622 {
6623 pDisk->PCHSGeometry.cCylinders = 0;
6624 pDisk->PCHSGeometry.cHeads = 0;
6625 pDisk->PCHSGeometry.cSectors = 0;
6626 }
6627 else
6628 {
6629 /* Make sure the CHS geometry is properly clipped. */
6630 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
6631 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
6632 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
6633 }
6634
6635 /* Cache LCHS geometry. */
6636 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
6637 &pDisk->LCHSGeometry);
6638 if (RT_FAILURE(rc2))
6639 {
6640 pDisk->LCHSGeometry.cCylinders = 0;
6641 pDisk->LCHSGeometry.cHeads = 0;
6642 pDisk->LCHSGeometry.cSectors = 0;
6643 }
6644 else
6645 {
6646 /* Make sure the CHS geometry is properly clipped. */
6647 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
6648 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
6649 }
6650
6651 /* Image successfully opened, make it the last image. */
6652 vdAddImageToList(pDisk, pImage);
6653 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
6654 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
6655 }
6656 else
6657 {
6658 /* Error detected, image may or may not be opened. Close and delete
6659 * image if it was opened. */
6660 if (pImage->pBackendData)
6661 {
6662 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, true);
6663 AssertRC(rc2);
6664 pImage->pBackendData = NULL;
6665 }
6666 }
6667 } while (0);
6668
6669 if (RT_UNLIKELY(fLockWrite))
6670 {
6671 rc2 = vdThreadFinishWrite(pDisk);
6672 AssertRC(rc2);
6673 }
6674 else if (RT_UNLIKELY(fLockRead))
6675 {
6676 rc2 = vdThreadFinishRead(pDisk);
6677 AssertRC(rc2);
6678 }
6679
6680 if (RT_FAILURE(rc))
6681 {
6682 if (pImage)
6683 {
6684 if (pImage->pszFilename)
6685 RTStrFree(pImage->pszFilename);
6686 RTMemFree(pImage);
6687 }
6688 }
6689
6690 if (RT_SUCCESS(rc) && pIfProgress && pIfProgress->pfnProgress)
6691 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
6692
6693 LogFlowFunc(("returns %Rrc\n", rc));
6694 return rc;
6695}
6696
6697/**
6698 * Creates and opens a new differencing image file in HDD container.
6699 * See comments for VDOpen function about differencing images.
6700 *
6701 * @returns VBox status code.
6702 * @param pDisk Pointer to HDD container.
6703 * @param pszBackend Name of the image file backend to use.
6704 * @param pszFilename Name of the differencing image file to create.
6705 * @param uImageFlags Flags specifying special image features.
6706 * @param pszComment Pointer to image comment. NULL is ok.
6707 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
6708 * @param pParentUuid New parent UUID of the image. If NULL, the UUID is queried automatically.
6709 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
6710 * @param pVDIfsImage Pointer to the per-image VD interface list.
6711 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
6712 */
6713VBOXDDU_DECL(int) VDCreateDiff(PVBOXHDD pDisk, const char *pszBackend,
6714 const char *pszFilename, unsigned uImageFlags,
6715 const char *pszComment, PCRTUUID pUuid,
6716 PCRTUUID pParentUuid, unsigned uOpenFlags,
6717 PVDINTERFACE pVDIfsImage,
6718 PVDINTERFACE pVDIfsOperation)
6719{
6720 int rc = VINF_SUCCESS;
6721 int rc2;
6722 bool fLockWrite = false, fLockRead = false;
6723 PVDIMAGE pImage = NULL;
6724 RTUUID uuid;
6725
6726 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
6727 pDisk, pszBackend, pszFilename, uImageFlags, pszComment, pUuid, uOpenFlags, pVDIfsImage, pVDIfsOperation));
6728
6729 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
6730
6731 do
6732 {
6733 /* sanity check */
6734 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6735 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6736
6737 /* Check arguments. */
6738 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
6739 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
6740 rc = VERR_INVALID_PARAMETER);
6741 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
6742 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
6743 rc = VERR_INVALID_PARAMETER);
6744 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
6745 ("uImageFlags=%#x\n", uImageFlags),
6746 rc = VERR_INVALID_PARAMETER);
6747 /* The UUID may be NULL. */
6748 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
6749 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
6750 rc = VERR_INVALID_PARAMETER);
6751 /* The parent UUID may be NULL. */
6752 AssertMsgBreakStmt(pParentUuid == NULL || VALID_PTR(pParentUuid),
6753 ("pParentUuid=%#p ParentUUID=%RTuuid\n", pParentUuid, pParentUuid),
6754 rc = VERR_INVALID_PARAMETER);
6755 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
6756 ("uOpenFlags=%#x\n", uOpenFlags),
6757 rc = VERR_INVALID_PARAMETER);
6758
6759 /* Check state. Needs a temporary read lock. Holding the write lock
6760 * all the time would be blocking other activities for too long. */
6761 rc2 = vdThreadStartRead(pDisk);
6762 AssertRC(rc2);
6763 fLockRead = true;
6764 AssertMsgBreakStmt(pDisk->cImages != 0,
6765 ("Create diff image cannot be done without other images open\n"),
6766 rc = VERR_VD_INVALID_STATE);
6767 rc2 = vdThreadFinishRead(pDisk);
6768 AssertRC(rc2);
6769 fLockRead = false;
6770
6771 /*
6772 * Destroy the current discard state first which might still have pending blocks
6773 * for the currently opened image which will be switched to readonly mode.
6774 */
6775 /* Lock disk for writing, as we modify pDisk information below. */
6776 rc2 = vdThreadStartWrite(pDisk);
6777 AssertRC(rc2);
6778 fLockWrite = true;
6779 rc = vdDiscardStateDestroy(pDisk);
6780 if (RT_FAILURE(rc))
6781 break;
6782 rc2 = vdThreadFinishWrite(pDisk);
6783 AssertRC(rc2);
6784 fLockWrite = false;
6785
6786 /* Set up image descriptor. */
6787 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
6788 if (!pImage)
6789 {
6790 rc = VERR_NO_MEMORY;
6791 break;
6792 }
6793 pImage->pszFilename = RTStrDup(pszFilename);
6794 if (!pImage->pszFilename)
6795 {
6796 rc = VERR_NO_MEMORY;
6797 break;
6798 }
6799
6800 rc = vdFindBackend(pszBackend, &pImage->Backend);
6801 if (RT_FAILURE(rc))
6802 break;
6803 if (!pImage->Backend)
6804 {
6805 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
6806 N_("VD: unknown backend name '%s'"), pszBackend);
6807 break;
6808 }
6809 if ( !(pImage->Backend->uBackendCaps & VD_CAP_DIFF)
6810 || !(pImage->Backend->uBackendCaps & ( VD_CAP_CREATE_FIXED
6811 | VD_CAP_CREATE_DYNAMIC)))
6812 {
6813 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
6814 N_("VD: backend '%s' cannot create diff images"), pszBackend);
6815 break;
6816 }
6817
6818 pImage->VDIo.pDisk = pDisk;
6819 pImage->pVDIfsImage = pVDIfsImage;
6820
6821 /* Set up the I/O interface. */
6822 pImage->VDIo.pInterfaceIo = VDIfIoGet(pVDIfsImage);
6823 if (!pImage->VDIo.pInterfaceIo)
6824 {
6825 vdIfIoFallbackCallbacksSetup(&pImage->VDIo.VDIfIo);
6826 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIo.Core, "VD_IO", VDINTERFACETYPE_IO,
6827 pDisk, sizeof(VDINTERFACEIO), &pVDIfsImage);
6828 pImage->VDIo.pInterfaceIo = &pImage->VDIo.VDIfIo;
6829 }
6830
6831 /* Set up the internal I/O interface. */
6832 AssertBreakStmt(!VDIfIoIntGet(pVDIfsImage), rc = VERR_INVALID_PARAMETER);
6833 vdIfIoIntCallbacksSetup(&pImage->VDIo.VDIfIoInt);
6834 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
6835 &pImage->VDIo, sizeof(VDINTERFACEIOINT), &pImage->pVDIfsImage);
6836 AssertRC(rc);
6837
6838 /* Create UUID if the caller didn't specify one. */
6839 if (!pUuid)
6840 {
6841 rc = RTUuidCreate(&uuid);
6842 if (RT_FAILURE(rc))
6843 {
6844 rc = vdError(pDisk, rc, RT_SRC_POS,
6845 N_("VD: cannot generate UUID for image '%s'"),
6846 pszFilename);
6847 break;
6848 }
6849 pUuid = &uuid;
6850 }
6851
6852 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
6853 pImage->VDIo.fIgnoreFlush = (uOpenFlags & VD_OPEN_FLAGS_IGNORE_FLUSH) != 0;
6854 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
6855 rc = pImage->Backend->pfnCreate(pImage->pszFilename, pDisk->cbSize,
6856 uImageFlags | VD_IMAGE_FLAGS_DIFF,
6857 pszComment, &pDisk->PCHSGeometry,
6858 &pDisk->LCHSGeometry, pUuid,
6859 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
6860 0, 99,
6861 pDisk->pVDIfsDisk,
6862 pImage->pVDIfsImage,
6863 pVDIfsOperation,
6864 &pImage->pBackendData);
6865
6866 if (RT_SUCCESS(rc))
6867 {
6868 pImage->VDIo.pBackendData = pImage->pBackendData;
6869 pImage->uImageFlags = uImageFlags;
6870
6871 /* Lock disk for writing, as we modify pDisk information below. */
6872 rc2 = vdThreadStartWrite(pDisk);
6873 AssertRC(rc2);
6874 fLockWrite = true;
6875
6876 /* Switch previous image to read-only mode. */
6877 unsigned uOpenFlagsPrevImg;
6878 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
6879 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
6880 {
6881 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
6882 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pBackendData, uOpenFlagsPrevImg);
6883 }
6884
6885 /** @todo optionally check UUIDs */
6886
6887 /* Re-check state, as the lock wasn't held and another image
6888 * creation call could have been done by another thread. */
6889 AssertMsgStmt(pDisk->cImages != 0,
6890 ("Create diff image cannot be done without other images open\n"),
6891 rc = VERR_VD_INVALID_STATE);
6892 }
6893
6894 if (RT_SUCCESS(rc))
6895 {
6896 RTUUID Uuid;
6897 RTTIMESPEC ts;
6898
6899 if (pParentUuid && !RTUuidIsNull(pParentUuid))
6900 {
6901 Uuid = *pParentUuid;
6902 pImage->Backend->pfnSetParentUuid(pImage->pBackendData, &Uuid);
6903 }
6904 else
6905 {
6906 rc2 = pDisk->pLast->Backend->pfnGetUuid(pDisk->pLast->pBackendData,
6907 &Uuid);
6908 if (RT_SUCCESS(rc2))
6909 pImage->Backend->pfnSetParentUuid(pImage->pBackendData, &Uuid);
6910 }
6911 rc2 = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
6912 &Uuid);
6913 if (RT_SUCCESS(rc2))
6914 pImage->Backend->pfnSetParentModificationUuid(pImage->pBackendData,
6915 &Uuid);
6916 if (pDisk->pLast->Backend->pfnGetTimeStamp)
6917 rc2 = pDisk->pLast->Backend->pfnGetTimeStamp(pDisk->pLast->pBackendData,
6918 &ts);
6919 else
6920 rc2 = VERR_NOT_IMPLEMENTED;
6921 if (RT_SUCCESS(rc2) && pImage->Backend->pfnSetParentTimeStamp)
6922 pImage->Backend->pfnSetParentTimeStamp(pImage->pBackendData, &ts);
6923
6924 if (pImage->Backend->pfnSetParentFilename)
6925 rc2 = pImage->Backend->pfnSetParentFilename(pImage->pBackendData, pDisk->pLast->pszFilename);
6926 }
6927
6928 if (RT_SUCCESS(rc))
6929 {
6930 /* Image successfully opened, make it the last image. */
6931 vdAddImageToList(pDisk, pImage);
6932 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
6933 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
6934 }
6935 else
6936 {
6937 /* Error detected, but image opened. Close and delete image. */
6938 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, true);
6939 AssertRC(rc2);
6940 pImage->pBackendData = NULL;
6941 }
6942 } while (0);
6943
6944 if (RT_UNLIKELY(fLockWrite))
6945 {
6946 rc2 = vdThreadFinishWrite(pDisk);
6947 AssertRC(rc2);
6948 }
6949 else if (RT_UNLIKELY(fLockRead))
6950 {
6951 rc2 = vdThreadFinishRead(pDisk);
6952 AssertRC(rc2);
6953 }
6954
6955 if (RT_FAILURE(rc))
6956 {
6957 if (pImage)
6958 {
6959 if (pImage->pszFilename)
6960 RTStrFree(pImage->pszFilename);
6961 RTMemFree(pImage);
6962 }
6963 }
6964
6965 if (RT_SUCCESS(rc) && pIfProgress && pIfProgress->pfnProgress)
6966 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
6967
6968 LogFlowFunc(("returns %Rrc\n", rc));
6969 return rc;
6970}
6971
6972
6973/**
6974 * Creates and opens new cache image file in HDD container.
6975 *
6976 * @return VBox status code.
6977 * @param pDisk Name of the cache file backend to use (case insensitive).
6978 * @param pszFilename Name of the differencing cache file to create.
6979 * @param cbSize Maximum size of the cache.
6980 * @param uImageFlags Flags specifying special cache features.
6981 * @param pszComment Pointer to image comment. NULL is ok.
6982 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
6983 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
6984 * @param pVDIfsCache Pointer to the per-cache VD interface list.
6985 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
6986 */
6987VBOXDDU_DECL(int) VDCreateCache(PVBOXHDD pDisk, const char *pszBackend,
6988 const char *pszFilename, uint64_t cbSize,
6989 unsigned uImageFlags, const char *pszComment,
6990 PCRTUUID pUuid, unsigned uOpenFlags,
6991 PVDINTERFACE pVDIfsCache, PVDINTERFACE pVDIfsOperation)
6992{
6993 int rc = VINF_SUCCESS;
6994 int rc2;
6995 bool fLockWrite = false, fLockRead = false;
6996 PVDCACHE pCache = NULL;
6997 RTUUID uuid;
6998
6999 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
7000 pDisk, pszBackend, pszFilename, cbSize, uImageFlags, pszComment, pUuid, uOpenFlags, pVDIfsCache, pVDIfsOperation));
7001
7002 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
7003
7004 do
7005 {
7006 /* sanity check */
7007 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7008 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7009
7010 /* Check arguments. */
7011 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
7012 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
7013 rc = VERR_INVALID_PARAMETER);
7014 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
7015 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
7016 rc = VERR_INVALID_PARAMETER);
7017 AssertMsgBreakStmt(cbSize,
7018 ("cbSize=%llu\n", cbSize),
7019 rc = VERR_INVALID_PARAMETER);
7020 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
7021 ("uImageFlags=%#x\n", uImageFlags),
7022 rc = VERR_INVALID_PARAMETER);
7023 /* The UUID may be NULL. */
7024 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
7025 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
7026 rc = VERR_INVALID_PARAMETER);
7027 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
7028 ("uOpenFlags=%#x\n", uOpenFlags),
7029 rc = VERR_INVALID_PARAMETER);
7030
7031 /* Check state. Needs a temporary read lock. Holding the write lock
7032 * all the time would be blocking other activities for too long. */
7033 rc2 = vdThreadStartRead(pDisk);
7034 AssertRC(rc2);
7035 fLockRead = true;
7036 AssertMsgBreakStmt(!pDisk->pCache,
7037 ("Create cache image cannot be done with a cache already attached\n"),
7038 rc = VERR_VD_CACHE_ALREADY_EXISTS);
7039 rc2 = vdThreadFinishRead(pDisk);
7040 AssertRC(rc2);
7041 fLockRead = false;
7042
7043 /* Set up image descriptor. */
7044 pCache = (PVDCACHE)RTMemAllocZ(sizeof(VDCACHE));
7045 if (!pCache)
7046 {
7047 rc = VERR_NO_MEMORY;
7048 break;
7049 }
7050 pCache->pszFilename = RTStrDup(pszFilename);
7051 if (!pCache->pszFilename)
7052 {
7053 rc = VERR_NO_MEMORY;
7054 break;
7055 }
7056
7057 rc = vdFindCacheBackend(pszBackend, &pCache->Backend);
7058 if (RT_FAILURE(rc))
7059 break;
7060 if (!pCache->Backend)
7061 {
7062 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
7063 N_("VD: unknown backend name '%s'"), pszBackend);
7064 break;
7065 }
7066
7067 pCache->VDIo.pDisk = pDisk;
7068 pCache->pVDIfsCache = pVDIfsCache;
7069
7070 /* Set up the I/O interface. */
7071 pCache->VDIo.pInterfaceIo = VDIfIoGet(pVDIfsCache);
7072 if (!pCache->VDIo.pInterfaceIo)
7073 {
7074 vdIfIoFallbackCallbacksSetup(&pCache->VDIo.VDIfIo);
7075 rc = VDInterfaceAdd(&pCache->VDIo.VDIfIo.Core, "VD_IO", VDINTERFACETYPE_IO,
7076 pDisk, sizeof(VDINTERFACEIO), &pVDIfsCache);
7077 pCache->VDIo.pInterfaceIo = &pCache->VDIo.VDIfIo;
7078 }
7079
7080 /* Set up the internal I/O interface. */
7081 AssertBreakStmt(!VDIfIoIntGet(pVDIfsCache), rc = VERR_INVALID_PARAMETER);
7082 vdIfIoIntCallbacksSetup(&pCache->VDIo.VDIfIoInt);
7083 rc = VDInterfaceAdd(&pCache->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
7084 &pCache->VDIo, sizeof(VDINTERFACEIOINT), &pCache->pVDIfsCache);
7085 AssertRC(rc);
7086
7087 /* Create UUID if the caller didn't specify one. */
7088 if (!pUuid)
7089 {
7090 rc = RTUuidCreate(&uuid);
7091 if (RT_FAILURE(rc))
7092 {
7093 rc = vdError(pDisk, rc, RT_SRC_POS,
7094 N_("VD: cannot generate UUID for image '%s'"),
7095 pszFilename);
7096 break;
7097 }
7098 pUuid = &uuid;
7099 }
7100
7101 pCache->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
7102 pCache->VDIo.fIgnoreFlush = (uOpenFlags & VD_OPEN_FLAGS_IGNORE_FLUSH) != 0;
7103 rc = pCache->Backend->pfnCreate(pCache->pszFilename, cbSize,
7104 uImageFlags,
7105 pszComment, pUuid,
7106 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
7107 0, 99,
7108 pDisk->pVDIfsDisk,
7109 pCache->pVDIfsCache,
7110 pVDIfsOperation,
7111 &pCache->pBackendData);
7112
7113 if (RT_SUCCESS(rc))
7114 {
7115 /* Lock disk for writing, as we modify pDisk information below. */
7116 rc2 = vdThreadStartWrite(pDisk);
7117 AssertRC(rc2);
7118 fLockWrite = true;
7119
7120 pCache->VDIo.pBackendData = pCache->pBackendData;
7121
7122 /* Re-check state, as the lock wasn't held and another image
7123 * creation call could have been done by another thread. */
7124 AssertMsgStmt(!pDisk->pCache,
7125 ("Create cache image cannot be done with another cache open\n"),
7126 rc = VERR_VD_CACHE_ALREADY_EXISTS);
7127 }
7128
7129 if ( RT_SUCCESS(rc)
7130 && pDisk->pLast)
7131 {
7132 RTUUID UuidModification;
7133
7134 /* Set same modification Uuid as the last image. */
7135 rc = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
7136 &UuidModification);
7137 if (RT_SUCCESS(rc))
7138 {
7139 rc = pCache->Backend->pfnSetModificationUuid(pCache->pBackendData,
7140 &UuidModification);
7141 }
7142
7143 if (rc == VERR_NOT_SUPPORTED)
7144 rc = VINF_SUCCESS;
7145 }
7146
7147 if (RT_SUCCESS(rc))
7148 {
7149 /* Cache successfully created. */
7150 pDisk->pCache = pCache;
7151 }
7152 else
7153 {
7154 /* Error detected, but image opened. Close and delete image. */
7155 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, true);
7156 AssertRC(rc2);
7157 pCache->pBackendData = NULL;
7158 }
7159 } while (0);
7160
7161 if (RT_UNLIKELY(fLockWrite))
7162 {
7163 rc2 = vdThreadFinishWrite(pDisk);
7164 AssertRC(rc2);
7165 }
7166 else if (RT_UNLIKELY(fLockRead))
7167 {
7168 rc2 = vdThreadFinishRead(pDisk);
7169 AssertRC(rc2);
7170 }
7171
7172 if (RT_FAILURE(rc))
7173 {
7174 if (pCache)
7175 {
7176 if (pCache->pszFilename)
7177 RTStrFree(pCache->pszFilename);
7178 RTMemFree(pCache);
7179 }
7180 }
7181
7182 if (RT_SUCCESS(rc) && pIfProgress && pIfProgress->pfnProgress)
7183 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
7184
7185 LogFlowFunc(("returns %Rrc\n", rc));
7186 return rc;
7187}
7188
7189/**
7190 * Merges two images (not necessarily with direct parent/child relationship).
7191 * As a side effect the source image and potentially the other images which
7192 * are also merged to the destination are deleted from both the disk and the
7193 * images in the HDD container.
7194 *
7195 * @returns VBox status code.
7196 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7197 * @param pDisk Pointer to HDD container.
7198 * @param nImageFrom Name of the image file to merge from.
7199 * @param nImageTo Name of the image file to merge to.
7200 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
7201 */
7202VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
7203 unsigned nImageTo, PVDINTERFACE pVDIfsOperation)
7204{
7205 int rc = VINF_SUCCESS;
7206 int rc2;
7207 bool fLockWrite = false, fLockRead = false;
7208 void *pvBuf = NULL;
7209
7210 LogFlowFunc(("pDisk=%#p nImageFrom=%u nImageTo=%u pVDIfsOperation=%#p\n",
7211 pDisk, nImageFrom, nImageTo, pVDIfsOperation));
7212
7213 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
7214
7215 do
7216 {
7217 /* sanity check */
7218 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7219 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7220
7221 /* For simplicity reasons lock for writing as the image reopen below
7222 * might need it. After all the reopen is usually needed. */
7223 rc2 = vdThreadStartWrite(pDisk);
7224 AssertRC(rc2);
7225 fLockWrite = true;
7226 PVDIMAGE pImageFrom = vdGetImageByNumber(pDisk, nImageFrom);
7227 PVDIMAGE pImageTo = vdGetImageByNumber(pDisk, nImageTo);
7228 if (!pImageFrom || !pImageTo)
7229 {
7230 rc = VERR_VD_IMAGE_NOT_FOUND;
7231 break;
7232 }
7233 AssertBreakStmt(pImageFrom != pImageTo, rc = VERR_INVALID_PARAMETER);
7234
7235 /* Make sure destination image is writable. */
7236 unsigned uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pBackendData);
7237 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
7238 {
7239 /*
7240 * Clear skip consistency checks because the image is made writable now and
7241 * skipping consistency checks is only possible for readonly images.
7242 */
7243 uOpenFlags &= ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS);
7244 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
7245 uOpenFlags);
7246 if (RT_FAILURE(rc))
7247 break;
7248 }
7249
7250 /* Get size of destination image. */
7251 uint64_t cbSize = pImageTo->Backend->pfnGetSize(pImageTo->pBackendData);
7252 rc2 = vdThreadFinishWrite(pDisk);
7253 AssertRC(rc2);
7254 fLockWrite = false;
7255
7256 /* Allocate tmp buffer. */
7257 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
7258 if (!pvBuf)
7259 {
7260 rc = VERR_NO_MEMORY;
7261 break;
7262 }
7263
7264 /* Merging is done directly on the images itself. This potentially
7265 * causes trouble if the disk is full in the middle of operation. */
7266 if (nImageFrom < nImageTo)
7267 {
7268 /* Merge parent state into child. This means writing all not
7269 * allocated blocks in the destination image which are allocated in
7270 * the images to be merged. */
7271 uint64_t uOffset = 0;
7272 uint64_t cbRemaining = cbSize;
7273 do
7274 {
7275 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
7276 RTSGSEG SegmentBuf;
7277 RTSGBUF SgBuf;
7278 VDIOCTX IoCtx;
7279
7280 SegmentBuf.pvSeg = pvBuf;
7281 SegmentBuf.cbSeg = VD_MERGE_BUFFER_SIZE;
7282 RTSgBufInit(&SgBuf, &SegmentBuf, 1);
7283 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_READ, 0, 0, NULL,
7284 &SgBuf, NULL, NULL, VDIOCTX_FLAGS_SYNC);
7285
7286 /* Need to hold the write lock during a read-write operation. */
7287 rc2 = vdThreadStartWrite(pDisk);
7288 AssertRC(rc2);
7289 fLockWrite = true;
7290
7291 rc = pImageTo->Backend->pfnRead(pImageTo->pBackendData,
7292 uOffset, cbThisRead,
7293 &IoCtx, &cbThisRead);
7294 if (rc == VERR_VD_BLOCK_FREE)
7295 {
7296 /* Search for image with allocated block. Do not attempt to
7297 * read more than the previous reads marked as valid.
7298 * Otherwise this would return stale data when different
7299 * block sizes are used for the images. */
7300 for (PVDIMAGE pCurrImage = pImageTo->pPrev;
7301 pCurrImage != NULL && pCurrImage != pImageFrom->pPrev && rc == VERR_VD_BLOCK_FREE;
7302 pCurrImage = pCurrImage->pPrev)
7303 {
7304 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
7305 uOffset, cbThisRead,
7306 &IoCtx, &cbThisRead);
7307 }
7308
7309 if (rc != VERR_VD_BLOCK_FREE)
7310 {
7311 if (RT_FAILURE(rc))
7312 break;
7313 /* Updating the cache is required because this might be a live merge. */
7314 rc = vdWriteHelperEx(pDisk, pImageTo, pImageFrom->pPrev,
7315 uOffset, pvBuf, cbThisRead,
7316 VDIOCTX_FLAGS_READ_UPDATE_CACHE, 0);
7317 if (RT_FAILURE(rc))
7318 break;
7319 }
7320 else
7321 rc = VINF_SUCCESS;
7322 }
7323 else if (RT_FAILURE(rc))
7324 break;
7325
7326 rc2 = vdThreadFinishWrite(pDisk);
7327 AssertRC(rc2);
7328 fLockWrite = false;
7329
7330 uOffset += cbThisRead;
7331 cbRemaining -= cbThisRead;
7332
7333 if (pIfProgress && pIfProgress->pfnProgress)
7334 {
7335 /** @todo r=klaus: this can update the progress to the same
7336 * percentage over and over again if the image format makes
7337 * relatively small increments. */
7338 rc = pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
7339 uOffset * 99 / cbSize);
7340 if (RT_FAILURE(rc))
7341 break;
7342 }
7343 } while (uOffset < cbSize);
7344 }
7345 else
7346 {
7347 /*
7348 * We may need to update the parent uuid of the child coming after
7349 * the last image to be merged. We have to reopen it read/write.
7350 *
7351 * This is done before we do the actual merge to prevent an
7352 * inconsistent chain if the mode change fails for some reason.
7353 */
7354 if (pImageFrom->pNext)
7355 {
7356 PVDIMAGE pImageChild = pImageFrom->pNext;
7357
7358 /* Take the write lock. */
7359 rc2 = vdThreadStartWrite(pDisk);
7360 AssertRC(rc2);
7361 fLockWrite = true;
7362
7363 /* We need to open the image in read/write mode. */
7364 uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pBackendData);
7365
7366 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
7367 {
7368 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
7369 rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pBackendData,
7370 uOpenFlags);
7371 if (RT_FAILURE(rc))
7372 break;
7373 }
7374
7375 rc2 = vdThreadFinishWrite(pDisk);
7376 AssertRC(rc2);
7377 fLockWrite = false;
7378 }
7379
7380 /* If the merge is from the last image we have to relay all writes
7381 * to the merge destination as well, so that concurrent writes
7382 * (in case of a live merge) are handled correctly. */
7383 if (!pImageFrom->pNext)
7384 {
7385 /* Take the write lock. */
7386 rc2 = vdThreadStartWrite(pDisk);
7387 AssertRC(rc2);
7388 fLockWrite = true;
7389
7390 pDisk->pImageRelay = pImageTo;
7391
7392 rc2 = vdThreadFinishWrite(pDisk);
7393 AssertRC(rc2);
7394 fLockWrite = false;
7395 }
7396
7397 /* Merge child state into parent. This means writing all blocks
7398 * which are allocated in the image up to the source image to the
7399 * destination image. */
7400 uint64_t uOffset = 0;
7401 uint64_t cbRemaining = cbSize;
7402 do
7403 {
7404 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
7405 RTSGSEG SegmentBuf;
7406 RTSGBUF SgBuf;
7407 VDIOCTX IoCtx;
7408
7409 rc = VERR_VD_BLOCK_FREE;
7410
7411 SegmentBuf.pvSeg = pvBuf;
7412 SegmentBuf.cbSeg = VD_MERGE_BUFFER_SIZE;
7413 RTSgBufInit(&SgBuf, &SegmentBuf, 1);
7414 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_READ, 0, 0, NULL,
7415 &SgBuf, NULL, NULL, VDIOCTX_FLAGS_SYNC);
7416
7417 /* Need to hold the write lock during a read-write operation. */
7418 rc2 = vdThreadStartWrite(pDisk);
7419 AssertRC(rc2);
7420 fLockWrite = true;
7421
7422 /* Search for image with allocated block. Do not attempt to
7423 * read more than the previous reads marked as valid. Otherwise
7424 * this would return stale data when different block sizes are
7425 * used for the images. */
7426 for (PVDIMAGE pCurrImage = pImageFrom;
7427 pCurrImage != NULL && pCurrImage != pImageTo && rc == VERR_VD_BLOCK_FREE;
7428 pCurrImage = pCurrImage->pPrev)
7429 {
7430 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
7431 uOffset, cbThisRead,
7432 &IoCtx, &cbThisRead);
7433 }
7434
7435 if (rc != VERR_VD_BLOCK_FREE)
7436 {
7437 if (RT_FAILURE(rc))
7438 break;
7439 rc = vdWriteHelper(pDisk, pImageTo, uOffset, pvBuf,
7440 cbThisRead, VDIOCTX_FLAGS_READ_UPDATE_CACHE);
7441 if (RT_FAILURE(rc))
7442 break;
7443 }
7444 else
7445 rc = VINF_SUCCESS;
7446
7447 rc2 = vdThreadFinishWrite(pDisk);
7448 AssertRC(rc2);
7449 fLockWrite = false;
7450
7451 uOffset += cbThisRead;
7452 cbRemaining -= cbThisRead;
7453
7454 if (pIfProgress && pIfProgress->pfnProgress)
7455 {
7456 /** @todo r=klaus: this can update the progress to the same
7457 * percentage over and over again if the image format makes
7458 * relatively small increments. */
7459 rc = pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
7460 uOffset * 99 / cbSize);
7461 if (RT_FAILURE(rc))
7462 break;
7463 }
7464 } while (uOffset < cbSize);
7465
7466 /* In case we set up a "write proxy" image above we must clear
7467 * this again now to prevent stray writes. Failure or not. */
7468 if (!pImageFrom->pNext)
7469 {
7470 /* Take the write lock. */
7471 rc2 = vdThreadStartWrite(pDisk);
7472 AssertRC(rc2);
7473 fLockWrite = true;
7474
7475 pDisk->pImageRelay = NULL;
7476
7477 rc2 = vdThreadFinishWrite(pDisk);
7478 AssertRC(rc2);
7479 fLockWrite = false;
7480 }
7481 }
7482
7483 /*
7484 * Leave in case of an error to avoid corrupted data in the image chain
7485 * (includes cancelling the operation by the user).
7486 */
7487 if (RT_FAILURE(rc))
7488 break;
7489
7490 /* Need to hold the write lock while finishing the merge. */
7491 rc2 = vdThreadStartWrite(pDisk);
7492 AssertRC(rc2);
7493 fLockWrite = true;
7494
7495 /* Update parent UUID so that image chain is consistent.
7496 * The two attempts work around the problem that some backends
7497 * (e.g. iSCSI) do not support UUIDs, so we exploit the fact that
7498 * so far there can only be one such image in the chain. */
7499 /** @todo needs a better long-term solution, passing the UUID
7500 * knowledge from the caller or some such */
7501 RTUUID Uuid;
7502 PVDIMAGE pImageChild = NULL;
7503 if (nImageFrom < nImageTo)
7504 {
7505 if (pImageFrom->pPrev)
7506 {
7507 /* plan A: ask the parent itself for its UUID */
7508 rc = pImageFrom->pPrev->Backend->pfnGetUuid(pImageFrom->pPrev->pBackendData,
7509 &Uuid);
7510 if (RT_FAILURE(rc))
7511 {
7512 /* plan B: ask the child of the parent for parent UUID */
7513 rc = pImageFrom->Backend->pfnGetParentUuid(pImageFrom->pBackendData,
7514 &Uuid);
7515 }
7516 AssertRC(rc);
7517 }
7518 else
7519 RTUuidClear(&Uuid);
7520 rc = pImageTo->Backend->pfnSetParentUuid(pImageTo->pBackendData,
7521 &Uuid);
7522 AssertRC(rc);
7523 }
7524 else
7525 {
7526 /* Update the parent uuid of the child of the last merged image. */
7527 if (pImageFrom->pNext)
7528 {
7529 /* plan A: ask the parent itself for its UUID */
7530 rc = pImageTo->Backend->pfnGetUuid(pImageTo->pBackendData,
7531 &Uuid);
7532 if (RT_FAILURE(rc))
7533 {
7534 /* plan B: ask the child of the parent for parent UUID */
7535 rc = pImageTo->pNext->Backend->pfnGetParentUuid(pImageTo->pNext->pBackendData,
7536 &Uuid);
7537 }
7538 AssertRC(rc);
7539
7540 rc = pImageFrom->Backend->pfnSetParentUuid(pImageFrom->pNext->pBackendData,
7541 &Uuid);
7542 AssertRC(rc);
7543
7544 pImageChild = pImageFrom->pNext;
7545 }
7546 }
7547
7548 /* Delete the no longer needed images. */
7549 PVDIMAGE pImg = pImageFrom, pTmp;
7550 while (pImg != pImageTo)
7551 {
7552 if (nImageFrom < nImageTo)
7553 pTmp = pImg->pNext;
7554 else
7555 pTmp = pImg->pPrev;
7556 vdRemoveImageFromList(pDisk, pImg);
7557 pImg->Backend->pfnClose(pImg->pBackendData, true);
7558 RTMemFree(pImg->pszFilename);
7559 RTMemFree(pImg);
7560 pImg = pTmp;
7561 }
7562
7563 /* Make sure destination image is back to read only if necessary. */
7564 if (pImageTo != pDisk->pLast)
7565 {
7566 uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pBackendData);
7567 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
7568 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
7569 uOpenFlags);
7570 if (RT_FAILURE(rc))
7571 break;
7572 }
7573
7574 /*
7575 * Make sure the child is readonly
7576 * for the child -> parent merge direction
7577 * if necessary.
7578 */
7579 if ( nImageFrom > nImageTo
7580 && pImageChild
7581 && pImageChild != pDisk->pLast)
7582 {
7583 uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pBackendData);
7584 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
7585 rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pBackendData,
7586 uOpenFlags);
7587 if (RT_FAILURE(rc))
7588 break;
7589 }
7590 } while (0);
7591
7592 if (RT_UNLIKELY(fLockWrite))
7593 {
7594 rc2 = vdThreadFinishWrite(pDisk);
7595 AssertRC(rc2);
7596 }
7597 else if (RT_UNLIKELY(fLockRead))
7598 {
7599 rc2 = vdThreadFinishRead(pDisk);
7600 AssertRC(rc2);
7601 }
7602
7603 if (pvBuf)
7604 RTMemTmpFree(pvBuf);
7605
7606 if (RT_SUCCESS(rc) && pIfProgress && pIfProgress->pfnProgress)
7607 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
7608
7609 LogFlowFunc(("returns %Rrc\n", rc));
7610 return rc;
7611}
7612
7613/**
7614 * Copies an image from one HDD container to another - extended version.
7615 * The copy is opened in the target HDD container.
7616 * It is possible to convert between different image formats, because the
7617 * backend for the destination may be different from the source.
7618 * If both the source and destination reference the same HDD container,
7619 * then the image is moved (by copying/deleting or renaming) to the new location.
7620 * The source container is unchanged if the move operation fails, otherwise
7621 * the image at the new location is opened in the same way as the old one was.
7622 *
7623 * @note The read/write accesses across disks are not synchronized, just the
7624 * accesses to each disk. Once there is a use case which requires a defined
7625 * read/write behavior in this situation this needs to be extended.
7626 *
7627 * @return VBox status code.
7628 * @return VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7629 * @param pDiskFrom Pointer to source HDD container.
7630 * @param nImage Image number, counts from 0. 0 is always base image of container.
7631 * @param pDiskTo Pointer to destination HDD container.
7632 * @param pszBackend Name of the image file backend to use (may be NULL to use the same as the source, case insensitive).
7633 * @param pszFilename New name of the image (may be NULL to specify that the
7634 * copy destination is the destination container, or
7635 * if pDiskFrom == pDiskTo, i.e. when moving).
7636 * @param fMoveByRename If true, attempt to perform a move by renaming (if successful the new size is ignored).
7637 * @param cbSize New image size (0 means leave unchanged).
7638 * @param nImageSameFrom todo
7639 * @param nImageSameTo todo
7640 * @param uImageFlags Flags specifying special destination image features.
7641 * @param pDstUuid New UUID of the destination image. If NULL, a new UUID is created.
7642 * This parameter is used if and only if a true copy is created.
7643 * In all rename/move cases or copy to existing image cases the modification UUIDs are copied over.
7644 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
7645 * Only used if the destination image is created.
7646 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
7647 * @param pDstVDIfsImage Pointer to the per-image VD interface list, for the
7648 * destination image.
7649 * @param pDstVDIfsOperation Pointer to the per-operation VD interface list,
7650 * for the destination operation.
7651 */
7652VBOXDDU_DECL(int) VDCopyEx(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
7653 const char *pszBackend, const char *pszFilename,
7654 bool fMoveByRename, uint64_t cbSize,
7655 unsigned nImageFromSame, unsigned nImageToSame,
7656 unsigned uImageFlags, PCRTUUID pDstUuid,
7657 unsigned uOpenFlags, PVDINTERFACE pVDIfsOperation,
7658 PVDINTERFACE pDstVDIfsImage,
7659 PVDINTERFACE pDstVDIfsOperation)
7660{
7661 int rc = VINF_SUCCESS;
7662 int rc2;
7663 bool fLockReadFrom = false, fLockWriteFrom = false, fLockWriteTo = false;
7664 PVDIMAGE pImageTo = NULL;
7665
7666 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",
7667 pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename, cbSize, nImageFromSame, nImageToSame, uImageFlags, pDstUuid, uOpenFlags, pVDIfsOperation, pDstVDIfsImage, pDstVDIfsOperation));
7668
7669 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
7670 PVDINTERFACEPROGRESS pDstIfProgress = VDIfProgressGet(pDstVDIfsOperation);
7671
7672 do {
7673 /* Check arguments. */
7674 AssertMsgBreakStmt(VALID_PTR(pDiskFrom), ("pDiskFrom=%#p\n", pDiskFrom),
7675 rc = VERR_INVALID_PARAMETER);
7676 AssertMsg(pDiskFrom->u32Signature == VBOXHDDDISK_SIGNATURE,
7677 ("u32Signature=%08x\n", pDiskFrom->u32Signature));
7678
7679 rc2 = vdThreadStartRead(pDiskFrom);
7680 AssertRC(rc2);
7681 fLockReadFrom = true;
7682 PVDIMAGE pImageFrom = vdGetImageByNumber(pDiskFrom, nImage);
7683 AssertPtrBreakStmt(pImageFrom, rc = VERR_VD_IMAGE_NOT_FOUND);
7684 AssertMsgBreakStmt(VALID_PTR(pDiskTo), ("pDiskTo=%#p\n", pDiskTo),
7685 rc = VERR_INVALID_PARAMETER);
7686 AssertMsg(pDiskTo->u32Signature == VBOXHDDDISK_SIGNATURE,
7687 ("u32Signature=%08x\n", pDiskTo->u32Signature));
7688 AssertMsgBreakStmt( (nImageFromSame < nImage || nImageFromSame == VD_IMAGE_CONTENT_UNKNOWN)
7689 && (nImageToSame < pDiskTo->cImages || nImageToSame == VD_IMAGE_CONTENT_UNKNOWN)
7690 && ( (nImageFromSame == VD_IMAGE_CONTENT_UNKNOWN && nImageToSame == VD_IMAGE_CONTENT_UNKNOWN)
7691 || (nImageFromSame != VD_IMAGE_CONTENT_UNKNOWN && nImageToSame != VD_IMAGE_CONTENT_UNKNOWN)),
7692 ("nImageFromSame=%u nImageToSame=%u\n", nImageFromSame, nImageToSame),
7693 rc = VERR_INVALID_PARAMETER);
7694
7695 /* Move the image. */
7696 if (pDiskFrom == pDiskTo)
7697 {
7698 /* Rename only works when backends are the same, are file based
7699 * and the rename method is implemented. */
7700 if ( fMoveByRename
7701 && !RTStrICmp(pszBackend, pImageFrom->Backend->pszBackendName)
7702 && pImageFrom->Backend->uBackendCaps & VD_CAP_FILE
7703 && pImageFrom->Backend->pfnRename)
7704 {
7705 rc2 = vdThreadFinishRead(pDiskFrom);
7706 AssertRC(rc2);
7707 fLockReadFrom = false;
7708
7709 rc2 = vdThreadStartWrite(pDiskFrom);
7710 AssertRC(rc2);
7711 fLockWriteFrom = true;
7712 rc = pImageFrom->Backend->pfnRename(pImageFrom->pBackendData, pszFilename ? pszFilename : pImageFrom->pszFilename);
7713 break;
7714 }
7715
7716 /** @todo Moving (including shrinking/growing) of the image is
7717 * requested, but the rename attempt failed or it wasn't possible.
7718 * Must now copy image to temp location. */
7719 AssertReleaseMsgFailed(("VDCopy: moving by copy/delete not implemented\n"));
7720 }
7721
7722 /* pszFilename is allowed to be NULL, as this indicates copy to the existing image. */
7723 AssertMsgBreakStmt(pszFilename == NULL || (VALID_PTR(pszFilename) && *pszFilename),
7724 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
7725 rc = VERR_INVALID_PARAMETER);
7726
7727 uint64_t cbSizeFrom;
7728 cbSizeFrom = pImageFrom->Backend->pfnGetSize(pImageFrom->pBackendData);
7729 if (cbSizeFrom == 0)
7730 {
7731 rc = VERR_VD_VALUE_NOT_FOUND;
7732 break;
7733 }
7734
7735 VDGEOMETRY PCHSGeometryFrom = {0, 0, 0};
7736 VDGEOMETRY LCHSGeometryFrom = {0, 0, 0};
7737 pImageFrom->Backend->pfnGetPCHSGeometry(pImageFrom->pBackendData, &PCHSGeometryFrom);
7738 pImageFrom->Backend->pfnGetLCHSGeometry(pImageFrom->pBackendData, &LCHSGeometryFrom);
7739
7740 RTUUID ImageUuid, ImageModificationUuid;
7741 if (pDiskFrom != pDiskTo)
7742 {
7743 if (pDstUuid)
7744 ImageUuid = *pDstUuid;
7745 else
7746 RTUuidCreate(&ImageUuid);
7747 }
7748 else
7749 {
7750 rc = pImageFrom->Backend->pfnGetUuid(pImageFrom->pBackendData, &ImageUuid);
7751 if (RT_FAILURE(rc))
7752 RTUuidCreate(&ImageUuid);
7753 }
7754 rc = pImageFrom->Backend->pfnGetModificationUuid(pImageFrom->pBackendData, &ImageModificationUuid);
7755 if (RT_FAILURE(rc))
7756 RTUuidClear(&ImageModificationUuid);
7757
7758 char szComment[1024];
7759 rc = pImageFrom->Backend->pfnGetComment(pImageFrom->pBackendData, szComment, sizeof(szComment));
7760 if (RT_FAILURE(rc))
7761 szComment[0] = '\0';
7762 else
7763 szComment[sizeof(szComment) - 1] = '\0';
7764
7765 rc2 = vdThreadFinishRead(pDiskFrom);
7766 AssertRC(rc2);
7767 fLockReadFrom = false;
7768
7769 rc2 = vdThreadStartRead(pDiskTo);
7770 AssertRC(rc2);
7771 unsigned cImagesTo = pDiskTo->cImages;
7772 rc2 = vdThreadFinishRead(pDiskTo);
7773 AssertRC(rc2);
7774
7775 if (pszFilename)
7776 {
7777 if (cbSize == 0)
7778 cbSize = cbSizeFrom;
7779
7780 /* Create destination image with the properties of source image. */
7781 /** @todo replace the VDCreateDiff/VDCreateBase calls by direct
7782 * calls to the backend. Unifies the code and reduces the API
7783 * dependencies. Would also make the synchronization explicit. */
7784 if (cImagesTo > 0)
7785 {
7786 rc = VDCreateDiff(pDiskTo, pszBackend, pszFilename,
7787 uImageFlags, szComment, &ImageUuid,
7788 NULL /* pParentUuid */,
7789 uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
7790 pDstVDIfsImage, NULL);
7791
7792 rc2 = vdThreadStartWrite(pDiskTo);
7793 AssertRC(rc2);
7794 fLockWriteTo = true;
7795 } else {
7796 /** @todo hack to force creation of a fixed image for
7797 * the RAW backend, which can't handle anything else. */
7798 if (!RTStrICmp(pszBackend, "RAW"))
7799 uImageFlags |= VD_IMAGE_FLAGS_FIXED;
7800
7801 vdFixupPCHSGeometry(&PCHSGeometryFrom, cbSize);
7802 vdFixupLCHSGeometry(&LCHSGeometryFrom, cbSize);
7803
7804 rc = VDCreateBase(pDiskTo, pszBackend, pszFilename, cbSize,
7805 uImageFlags, szComment,
7806 &PCHSGeometryFrom, &LCHSGeometryFrom,
7807 NULL, uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
7808 pDstVDIfsImage, NULL);
7809
7810 rc2 = vdThreadStartWrite(pDiskTo);
7811 AssertRC(rc2);
7812 fLockWriteTo = true;
7813
7814 if (RT_SUCCESS(rc) && !RTUuidIsNull(&ImageUuid))
7815 pDiskTo->pLast->Backend->pfnSetUuid(pDiskTo->pLast->pBackendData, &ImageUuid);
7816 }
7817 if (RT_FAILURE(rc))
7818 break;
7819
7820 pImageTo = pDiskTo->pLast;
7821 AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
7822
7823 cbSize = RT_MIN(cbSize, cbSizeFrom);
7824 }
7825 else
7826 {
7827 pImageTo = pDiskTo->pLast;
7828 AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
7829
7830 uint64_t cbSizeTo;
7831 cbSizeTo = pImageTo->Backend->pfnGetSize(pImageTo->pBackendData);
7832 if (cbSizeTo == 0)
7833 {
7834 rc = VERR_VD_VALUE_NOT_FOUND;
7835 break;
7836 }
7837
7838 if (cbSize == 0)
7839 cbSize = RT_MIN(cbSizeFrom, cbSizeTo);
7840
7841 vdFixupPCHSGeometry(&PCHSGeometryFrom, cbSize);
7842 vdFixupLCHSGeometry(&LCHSGeometryFrom, cbSize);
7843
7844 /* Update the geometry in the destination image. */
7845 pImageTo->Backend->pfnSetPCHSGeometry(pImageTo->pBackendData, &PCHSGeometryFrom);
7846 pImageTo->Backend->pfnSetLCHSGeometry(pImageTo->pBackendData, &LCHSGeometryFrom);
7847 }
7848
7849 rc2 = vdThreadFinishWrite(pDiskTo);
7850 AssertRC(rc2);
7851 fLockWriteTo = false;
7852
7853 /* Whether we can take the optimized copy path (false) or not.
7854 * Don't optimize if the image existed or if it is a child image. */
7855 bool fSuppressRedundantIo = ( !(pszFilename == NULL || cImagesTo > 0)
7856 || (nImageToSame != VD_IMAGE_CONTENT_UNKNOWN));
7857 unsigned cImagesFromReadBack, cImagesToReadBack;
7858
7859 if (nImageFromSame == VD_IMAGE_CONTENT_UNKNOWN)
7860 cImagesFromReadBack = 0;
7861 else
7862 {
7863 if (nImage == VD_LAST_IMAGE)
7864 cImagesFromReadBack = pDiskFrom->cImages - nImageFromSame - 1;
7865 else
7866 cImagesFromReadBack = nImage - nImageFromSame;
7867 }
7868
7869 if (nImageToSame == VD_IMAGE_CONTENT_UNKNOWN)
7870 cImagesToReadBack = 0;
7871 else
7872 cImagesToReadBack = pDiskTo->cImages - nImageToSame - 1;
7873
7874 /* Copy the data. */
7875 rc = vdCopyHelper(pDiskFrom, pImageFrom, pDiskTo, cbSize,
7876 cImagesFromReadBack, cImagesToReadBack,
7877 fSuppressRedundantIo, pIfProgress, pDstIfProgress);
7878
7879 if (RT_SUCCESS(rc))
7880 {
7881 rc2 = vdThreadStartWrite(pDiskTo);
7882 AssertRC(rc2);
7883 fLockWriteTo = true;
7884
7885 /* Only set modification UUID if it is non-null, since the source
7886 * backend might not provide a valid modification UUID. */
7887 if (!RTUuidIsNull(&ImageModificationUuid))
7888 pImageTo->Backend->pfnSetModificationUuid(pImageTo->pBackendData, &ImageModificationUuid);
7889
7890 /* Set the requested open flags if they differ from the value
7891 * required for creating the image and copying the contents. */
7892 if ( pImageTo && pszFilename
7893 && uOpenFlags != (uOpenFlags & ~VD_OPEN_FLAGS_READONLY))
7894 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
7895 uOpenFlags);
7896 }
7897 } while (0);
7898
7899 if (RT_FAILURE(rc) && pImageTo && pszFilename)
7900 {
7901 /* Take the write lock only if it is not taken. Not worth making the
7902 * above code even more complicated. */
7903 if (RT_UNLIKELY(!fLockWriteTo))
7904 {
7905 rc2 = vdThreadStartWrite(pDiskTo);
7906 AssertRC(rc2);
7907 fLockWriteTo = true;
7908 }
7909 /* Error detected, but new image created. Remove image from list. */
7910 vdRemoveImageFromList(pDiskTo, pImageTo);
7911
7912 /* Close and delete image. */
7913 rc2 = pImageTo->Backend->pfnClose(pImageTo->pBackendData, true);
7914 AssertRC(rc2);
7915 pImageTo->pBackendData = NULL;
7916
7917 /* Free remaining resources. */
7918 if (pImageTo->pszFilename)
7919 RTStrFree(pImageTo->pszFilename);
7920
7921 RTMemFree(pImageTo);
7922 }
7923
7924 if (RT_UNLIKELY(fLockWriteTo))
7925 {
7926 rc2 = vdThreadFinishWrite(pDiskTo);
7927 AssertRC(rc2);
7928 }
7929 if (RT_UNLIKELY(fLockWriteFrom))
7930 {
7931 rc2 = vdThreadFinishWrite(pDiskFrom);
7932 AssertRC(rc2);
7933 }
7934 else if (RT_UNLIKELY(fLockReadFrom))
7935 {
7936 rc2 = vdThreadFinishRead(pDiskFrom);
7937 AssertRC(rc2);
7938 }
7939
7940 if (RT_SUCCESS(rc))
7941 {
7942 if (pIfProgress && pIfProgress->pfnProgress)
7943 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
7944 if (pDstIfProgress && pDstIfProgress->pfnProgress)
7945 pDstIfProgress->pfnProgress(pDstIfProgress->Core.pvUser, 100);
7946 }
7947
7948 LogFlowFunc(("returns %Rrc\n", rc));
7949 return rc;
7950}
7951
7952/**
7953 * Copies an image from one HDD container to another.
7954 * The copy is opened in the target HDD container.
7955 * It is possible to convert between different image formats, because the
7956 * backend for the destination may be different from the source.
7957 * If both the source and destination reference the same HDD container,
7958 * then the image is moved (by copying/deleting or renaming) to the new location.
7959 * The source container is unchanged if the move operation fails, otherwise
7960 * the image at the new location is opened in the same way as the old one was.
7961 *
7962 * @returns VBox status code.
7963 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7964 * @param pDiskFrom Pointer to source HDD container.
7965 * @param nImage Image number, counts from 0. 0 is always base image of container.
7966 * @param pDiskTo Pointer to destination HDD container.
7967 * @param pszBackend Name of the image file backend to use.
7968 * @param pszFilename New name of the image (may be NULL if pDiskFrom == pDiskTo).
7969 * @param fMoveByRename If true, attempt to perform a move by renaming (if successful the new size is ignored).
7970 * @param cbSize New image size (0 means leave unchanged).
7971 * @param uImageFlags Flags specifying special destination image features.
7972 * @param pDstUuid New UUID of the destination image. If NULL, a new UUID is created.
7973 * This parameter is used if and only if a true copy is created.
7974 * In all rename/move cases the UUIDs are copied over.
7975 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
7976 * Only used if the destination image is created.
7977 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
7978 * @param pDstVDIfsImage Pointer to the per-image VD interface list, for the
7979 * destination image.
7980 * @param pDstVDIfsOperation Pointer to the per-image VD interface list,
7981 * for the destination image.
7982 */
7983VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
7984 const char *pszBackend, const char *pszFilename,
7985 bool fMoveByRename, uint64_t cbSize,
7986 unsigned uImageFlags, PCRTUUID pDstUuid,
7987 unsigned uOpenFlags, PVDINTERFACE pVDIfsOperation,
7988 PVDINTERFACE pDstVDIfsImage,
7989 PVDINTERFACE pDstVDIfsOperation)
7990{
7991 return VDCopyEx(pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename,
7992 cbSize, VD_IMAGE_CONTENT_UNKNOWN, VD_IMAGE_CONTENT_UNKNOWN,
7993 uImageFlags, pDstUuid, uOpenFlags, pVDIfsOperation,
7994 pDstVDIfsImage, pDstVDIfsOperation);
7995}
7996
7997/**
7998 * Optimizes the storage consumption of an image. Typically the unused blocks
7999 * have to be wiped with zeroes to achieve a substantial reduced storage use.
8000 * Another optimization done is reordering the image blocks, which can provide
8001 * a significant performance boost, as reads and writes tend to use less random
8002 * file offsets.
8003 *
8004 * @return VBox status code.
8005 * @return VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
8006 * @return VERR_VD_IMAGE_READ_ONLY if image is not writable.
8007 * @return VERR_NOT_SUPPORTED if this kind of image can be compacted, but
8008 * the code for this isn't implemented yet.
8009 * @param pDisk Pointer to HDD container.
8010 * @param nImage Image number, counts from 0. 0 is always base image of container.
8011 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
8012 */
8013VBOXDDU_DECL(int) VDCompact(PVBOXHDD pDisk, unsigned nImage,
8014 PVDINTERFACE pVDIfsOperation)
8015{
8016 int rc = VINF_SUCCESS;
8017 int rc2;
8018 bool fLockRead = false, fLockWrite = false;
8019 void *pvBuf = NULL;
8020 void *pvTmp = NULL;
8021
8022 LogFlowFunc(("pDisk=%#p nImage=%u pVDIfsOperation=%#p\n",
8023 pDisk, nImage, pVDIfsOperation));
8024
8025 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
8026
8027 do {
8028 /* Check arguments. */
8029 AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
8030 rc = VERR_INVALID_PARAMETER);
8031 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE,
8032 ("u32Signature=%08x\n", pDisk->u32Signature));
8033
8034 rc2 = vdThreadStartRead(pDisk);
8035 AssertRC(rc2);
8036 fLockRead = true;
8037
8038 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8039 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
8040
8041 /* If there is no compact callback for not file based backends then
8042 * the backend doesn't need compaction. No need to make much fuss about
8043 * this. For file based ones signal this as not yet supported. */
8044 if (!pImage->Backend->pfnCompact)
8045 {
8046 if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
8047 rc = VERR_NOT_SUPPORTED;
8048 else
8049 rc = VINF_SUCCESS;
8050 break;
8051 }
8052
8053 /* Insert interface for reading parent state into per-operation list,
8054 * if there is a parent image. */
8055 VDINTERFACEPARENTSTATE VDIfParent;
8056 VDPARENTSTATEDESC ParentUser;
8057 if (pImage->pPrev)
8058 {
8059 VDIfParent.pfnParentRead = vdParentRead;
8060 ParentUser.pDisk = pDisk;
8061 ParentUser.pImage = pImage->pPrev;
8062 rc = VDInterfaceAdd(&VDIfParent.Core, "VDCompact_ParentState", VDINTERFACETYPE_PARENTSTATE,
8063 &ParentUser, sizeof(VDINTERFACEPARENTSTATE), &pVDIfsOperation);
8064 AssertRC(rc);
8065 }
8066
8067 rc2 = vdThreadFinishRead(pDisk);
8068 AssertRC(rc2);
8069 fLockRead = false;
8070
8071 rc2 = vdThreadStartWrite(pDisk);
8072 AssertRC(rc2);
8073 fLockWrite = true;
8074
8075 rc = pImage->Backend->pfnCompact(pImage->pBackendData,
8076 0, 99,
8077 pDisk->pVDIfsDisk,
8078 pImage->pVDIfsImage,
8079 pVDIfsOperation);
8080 } while (0);
8081
8082 if (RT_UNLIKELY(fLockWrite))
8083 {
8084 rc2 = vdThreadFinishWrite(pDisk);
8085 AssertRC(rc2);
8086 }
8087 else if (RT_UNLIKELY(fLockRead))
8088 {
8089 rc2 = vdThreadFinishRead(pDisk);
8090 AssertRC(rc2);
8091 }
8092
8093 if (pvBuf)
8094 RTMemTmpFree(pvBuf);
8095 if (pvTmp)
8096 RTMemTmpFree(pvTmp);
8097
8098 if (RT_SUCCESS(rc))
8099 {
8100 if (pIfProgress && pIfProgress->pfnProgress)
8101 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
8102 }
8103
8104 LogFlowFunc(("returns %Rrc\n", rc));
8105 return rc;
8106}
8107
8108/**
8109 * Resizes the given disk image to the given size.
8110 *
8111 * @return VBox status
8112 * @return VERR_VD_IMAGE_READ_ONLY if image is not writable.
8113 * @return VERR_NOT_SUPPORTED if this kind of image can be compacted, but
8114 *
8115 * @param pDisk Pointer to the HDD container.
8116 * @param cbSize New size of the image.
8117 * @param pPCHSGeometry Pointer to the new physical disk geometry <= (16383,16,63). Not NULL.
8118 * @param pLCHSGeometry Pointer to the new logical disk geometry <= (x,255,63). Not NULL.
8119 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
8120 */
8121VBOXDDU_DECL(int) VDResize(PVBOXHDD pDisk, uint64_t cbSize,
8122 PCVDGEOMETRY pPCHSGeometry,
8123 PCVDGEOMETRY pLCHSGeometry,
8124 PVDINTERFACE pVDIfsOperation)
8125{
8126 /** @todo r=klaus resizing was designed to be part of VDCopy, so having a separate function is not desirable. */
8127 int rc = VINF_SUCCESS;
8128 int rc2;
8129 bool fLockRead = false, fLockWrite = false;
8130
8131 LogFlowFunc(("pDisk=%#p cbSize=%llu pVDIfsOperation=%#p\n",
8132 pDisk, cbSize, pVDIfsOperation));
8133
8134 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
8135
8136 do {
8137 /* Check arguments. */
8138 AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
8139 rc = VERR_INVALID_PARAMETER);
8140 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE,
8141 ("u32Signature=%08x\n", pDisk->u32Signature));
8142
8143 rc2 = vdThreadStartRead(pDisk);
8144 AssertRC(rc2);
8145 fLockRead = true;
8146
8147 /* Must have at least one image in the chain, will resize last. */
8148 AssertMsgBreakStmt(pDisk->cImages >= 1, ("cImages=%u\n", pDisk->cImages),
8149 rc = VERR_NOT_SUPPORTED);
8150
8151 PVDIMAGE pImage = pDisk->pLast;
8152
8153 /* If there is no compact callback for not file based backends then
8154 * the backend doesn't need compaction. No need to make much fuss about
8155 * this. For file based ones signal this as not yet supported. */
8156 if (!pImage->Backend->pfnResize)
8157 {
8158 if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
8159 rc = VERR_NOT_SUPPORTED;
8160 else
8161 rc = VINF_SUCCESS;
8162 break;
8163 }
8164
8165 rc2 = vdThreadFinishRead(pDisk);
8166 AssertRC(rc2);
8167 fLockRead = false;
8168
8169 rc2 = vdThreadStartWrite(pDisk);
8170 AssertRC(rc2);
8171 fLockWrite = true;
8172
8173 VDGEOMETRY PCHSGeometryOld;
8174 VDGEOMETRY LCHSGeometryOld;
8175 PCVDGEOMETRY pPCHSGeometryNew;
8176 PCVDGEOMETRY pLCHSGeometryNew;
8177
8178 if (pPCHSGeometry->cCylinders == 0)
8179 {
8180 /* Auto-detect marker, calculate new value ourself. */
8181 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData, &PCHSGeometryOld);
8182 if (RT_SUCCESS(rc) && (PCHSGeometryOld.cCylinders != 0))
8183 PCHSGeometryOld.cCylinders = RT_MIN(cbSize / 512 / PCHSGeometryOld.cHeads / PCHSGeometryOld.cSectors, 16383);
8184 else if (rc == VERR_VD_GEOMETRY_NOT_SET)
8185 rc = VINF_SUCCESS;
8186
8187 pPCHSGeometryNew = &PCHSGeometryOld;
8188 }
8189 else
8190 pPCHSGeometryNew = pPCHSGeometry;
8191
8192 if (pLCHSGeometry->cCylinders == 0)
8193 {
8194 /* Auto-detect marker, calculate new value ourself. */
8195 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData, &LCHSGeometryOld);
8196 if (RT_SUCCESS(rc) && (LCHSGeometryOld.cCylinders != 0))
8197 LCHSGeometryOld.cCylinders = cbSize / 512 / LCHSGeometryOld.cHeads / LCHSGeometryOld.cSectors;
8198 else if (rc == VERR_VD_GEOMETRY_NOT_SET)
8199 rc = VINF_SUCCESS;
8200
8201 pLCHSGeometryNew = &LCHSGeometryOld;
8202 }
8203 else
8204 pLCHSGeometryNew = pLCHSGeometry;
8205
8206 if (RT_SUCCESS(rc))
8207 rc = pImage->Backend->pfnResize(pImage->pBackendData,
8208 cbSize,
8209 pPCHSGeometryNew,
8210 pLCHSGeometryNew,
8211 0, 99,
8212 pDisk->pVDIfsDisk,
8213 pImage->pVDIfsImage,
8214 pVDIfsOperation);
8215 } while (0);
8216
8217 if (RT_UNLIKELY(fLockWrite))
8218 {
8219 rc2 = vdThreadFinishWrite(pDisk);
8220 AssertRC(rc2);
8221 }
8222 else if (RT_UNLIKELY(fLockRead))
8223 {
8224 rc2 = vdThreadFinishRead(pDisk);
8225 AssertRC(rc2);
8226 }
8227
8228 if (RT_SUCCESS(rc))
8229 {
8230 if (pIfProgress && pIfProgress->pfnProgress)
8231 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
8232
8233 pDisk->cbSize = cbSize;
8234 }
8235
8236 LogFlowFunc(("returns %Rrc\n", rc));
8237 return rc;
8238}
8239
8240/**
8241 * Closes the last opened image file in HDD container.
8242 * If previous image file was opened in read-only mode (the normal case) and
8243 * the last opened image is in read-write mode then the previous image will be
8244 * reopened in read/write mode.
8245 *
8246 * @returns VBox status code.
8247 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
8248 * @param pDisk Pointer to HDD container.
8249 * @param fDelete If true, delete the image from the host disk.
8250 */
8251VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete)
8252{
8253 int rc = VINF_SUCCESS;
8254 int rc2;
8255 bool fLockWrite = false;
8256
8257 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
8258 do
8259 {
8260 /* sanity check */
8261 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8262 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8263
8264 /* Not worth splitting this up into a read lock phase and write
8265 * lock phase, as closing an image is a relatively fast operation
8266 * dominated by the part which needs the write lock. */
8267 rc2 = vdThreadStartWrite(pDisk);
8268 AssertRC(rc2);
8269 fLockWrite = true;
8270
8271 PVDIMAGE pImage = pDisk->pLast;
8272 if (!pImage)
8273 {
8274 rc = VERR_VD_NOT_OPENED;
8275 break;
8276 }
8277
8278 /* Destroy the current discard state first which might still have pending blocks. */
8279 rc = vdDiscardStateDestroy(pDisk);
8280 if (RT_FAILURE(rc))
8281 break;
8282
8283 unsigned uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
8284 /* Remove image from list of opened images. */
8285 vdRemoveImageFromList(pDisk, pImage);
8286 /* Close (and optionally delete) image. */
8287 rc = pImage->Backend->pfnClose(pImage->pBackendData, fDelete);
8288 /* Free remaining resources related to the image. */
8289 RTStrFree(pImage->pszFilename);
8290 RTMemFree(pImage);
8291
8292 pImage = pDisk->pLast;
8293 if (!pImage)
8294 break;
8295
8296 /* If disk was previously in read/write mode, make sure it will stay
8297 * like this (if possible) after closing this image. Set the open flags
8298 * accordingly. */
8299 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
8300 {
8301 uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
8302 uOpenFlags &= ~ VD_OPEN_FLAGS_READONLY;
8303 rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData, uOpenFlags);
8304 }
8305
8306 /* Cache disk information. */
8307 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
8308
8309 /* Cache PCHS geometry. */
8310 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
8311 &pDisk->PCHSGeometry);
8312 if (RT_FAILURE(rc2))
8313 {
8314 pDisk->PCHSGeometry.cCylinders = 0;
8315 pDisk->PCHSGeometry.cHeads = 0;
8316 pDisk->PCHSGeometry.cSectors = 0;
8317 }
8318 else
8319 {
8320 /* Make sure the PCHS geometry is properly clipped. */
8321 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
8322 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
8323 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
8324 }
8325
8326 /* Cache LCHS geometry. */
8327 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
8328 &pDisk->LCHSGeometry);
8329 if (RT_FAILURE(rc2))
8330 {
8331 pDisk->LCHSGeometry.cCylinders = 0;
8332 pDisk->LCHSGeometry.cHeads = 0;
8333 pDisk->LCHSGeometry.cSectors = 0;
8334 }
8335 else
8336 {
8337 /* Make sure the LCHS geometry is properly clipped. */
8338 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
8339 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
8340 }
8341 } while (0);
8342
8343 if (RT_UNLIKELY(fLockWrite))
8344 {
8345 rc2 = vdThreadFinishWrite(pDisk);
8346 AssertRC(rc2);
8347 }
8348
8349 LogFlowFunc(("returns %Rrc\n", rc));
8350 return rc;
8351}
8352
8353/**
8354 * Closes the currently opened cache image file in HDD container.
8355 *
8356 * @return VBox status code.
8357 * @return VERR_VD_NOT_OPENED if no cache is opened in HDD container.
8358 * @param pDisk Pointer to HDD container.
8359 * @param fDelete If true, delete the image from the host disk.
8360 */
8361VBOXDDU_DECL(int) VDCacheClose(PVBOXHDD pDisk, bool fDelete)
8362{
8363 int rc = VINF_SUCCESS;
8364 int rc2;
8365 bool fLockWrite = false;
8366 PVDCACHE pCache = NULL;
8367
8368 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
8369
8370 do
8371 {
8372 /* sanity check */
8373 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8374 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8375
8376 rc2 = vdThreadStartWrite(pDisk);
8377 AssertRC(rc2);
8378 fLockWrite = true;
8379
8380 AssertPtrBreakStmt(pDisk->pCache, rc = VERR_VD_CACHE_NOT_FOUND);
8381
8382 pCache = pDisk->pCache;
8383 pDisk->pCache = NULL;
8384
8385 pCache->Backend->pfnClose(pCache->pBackendData, fDelete);
8386 if (pCache->pszFilename)
8387 RTStrFree(pCache->pszFilename);
8388 RTMemFree(pCache);
8389 } while (0);
8390
8391 if (RT_LIKELY(fLockWrite))
8392 {
8393 rc2 = vdThreadFinishWrite(pDisk);
8394 AssertRC(rc2);
8395 }
8396
8397 LogFlowFunc(("returns %Rrc\n", rc));
8398 return rc;
8399}
8400
8401/**
8402 * Removes the last added filter in the HDD container.
8403 *
8404 * @return VBox status code.
8405 * @retval VERR_VD_NOT_OPENED if no filter is present for the disk.
8406 * @param pDisk Pointer to HDD container.
8407 */
8408VBOXDDU_DECL(int) VDFilterRemove(PVBOXHDD pDisk)
8409{
8410 int rc = VINF_SUCCESS;
8411 int rc2;
8412 bool fLockWrite = false;
8413 PVDFILTER pFilter = NULL;
8414
8415 LogFlowFunc(("pDisk=%#p\n", pDisk));
8416
8417 do
8418 {
8419 /* sanity check */
8420 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8421 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8422
8423 rc2 = vdThreadStartWrite(pDisk);
8424 AssertRC(rc2);
8425 fLockWrite = true;
8426
8427 AssertPtrBreakStmt(pDisk->pFilterHead, rc = VERR_VD_NOT_OPENED);
8428
8429 pFilter = pDisk->pFilterTail;
8430 vdRemoveFilterFromList(pDisk, pFilter);
8431
8432 pFilter->pBackend->pfnDestroy(pFilter->pvBackendData);
8433 RTMemFree(pFilter);
8434 } while (0);
8435
8436 if (RT_LIKELY(fLockWrite))
8437 {
8438 rc2 = vdThreadFinishWrite(pDisk);
8439 AssertRC(rc2);
8440 }
8441
8442 LogFlowFunc(("returns %Rrc\n", rc));
8443 return rc;
8444}
8445
8446/**
8447 * Closes all opened image files in HDD container.
8448 *
8449 * @returns VBox status code.
8450 * @param pDisk Pointer to HDD container.
8451 */
8452VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk)
8453{
8454 int rc = VINF_SUCCESS;
8455 int rc2;
8456 bool fLockWrite = false;
8457
8458 LogFlowFunc(("pDisk=%#p\n", pDisk));
8459 do
8460 {
8461 /* sanity check */
8462 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8463 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8464
8465 /* Lock the entire operation. */
8466 rc2 = vdThreadStartWrite(pDisk);
8467 AssertRC(rc2);
8468 fLockWrite = true;
8469
8470 PVDCACHE pCache = pDisk->pCache;
8471 if (pCache)
8472 {
8473 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, false);
8474 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
8475 rc = rc2;
8476
8477 if (pCache->pszFilename)
8478 RTStrFree(pCache->pszFilename);
8479 RTMemFree(pCache);
8480 }
8481
8482 PVDIMAGE pImage = pDisk->pLast;
8483 while (VALID_PTR(pImage))
8484 {
8485 PVDIMAGE pPrev = pImage->pPrev;
8486 /* Remove image from list of opened images. */
8487 vdRemoveImageFromList(pDisk, pImage);
8488 /* Close image. */
8489 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, false);
8490 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
8491 rc = rc2;
8492 /* Free remaining resources related to the image. */
8493 RTStrFree(pImage->pszFilename);
8494 RTMemFree(pImage);
8495 pImage = pPrev;
8496 }
8497 Assert(!VALID_PTR(pDisk->pLast));
8498 } while (0);
8499
8500 if (RT_UNLIKELY(fLockWrite))
8501 {
8502 rc2 = vdThreadFinishWrite(pDisk);
8503 AssertRC(rc2);
8504 }
8505
8506 LogFlowFunc(("returns %Rrc\n", rc));
8507 return rc;
8508}
8509
8510/**
8511 * Removes all filters of the given HDD container.
8512 *
8513 * @return VBox status code.
8514 * @param pDisk Pointer to HDD container.
8515 */
8516VBOXDDU_DECL(int) VDFilterRemoveAll(PVBOXHDD pDisk)
8517{
8518 int rc = VINF_SUCCESS;
8519 int rc2;
8520 bool fLockWrite = false;
8521
8522 LogFlowFunc(("pDisk=%#p\n", pDisk));
8523 do
8524 {
8525 /* sanity check */
8526 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8527 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8528
8529 /* Lock the entire operation. */
8530 rc2 = vdThreadStartWrite(pDisk);
8531 AssertRC(rc2);
8532 fLockWrite = true;
8533
8534 PVDFILTER pFilter = pDisk->pFilterTail;
8535 while (VALID_PTR(pFilter))
8536 {
8537 PVDFILTER pPrev = pFilter->pPrev;
8538 vdRemoveFilterFromList(pDisk, pFilter);
8539
8540 rc2 = pFilter->pBackend->pfnDestroy(pFilter->pvBackendData);
8541 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
8542 rc = rc2;
8543 /* Free remaining resources related to the image. */
8544 RTMemFree(pFilter);
8545 pFilter = pPrev;
8546 }
8547 Assert(!VALID_PTR(pDisk->pFilterTail));
8548 } while (0);
8549
8550 if (RT_UNLIKELY(fLockWrite))
8551 {
8552 rc2 = vdThreadFinishWrite(pDisk);
8553 AssertRC(rc2);
8554 }
8555
8556 LogFlowFunc(("returns %Rrc\n", rc));
8557 return rc;
8558}
8559
8560/**
8561 * Read data from virtual HDD.
8562 *
8563 * @returns VBox status code.
8564 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
8565 * @param pDisk Pointer to HDD container.
8566 * @param uOffset Offset of first reading byte from start of disk.
8567 * @param pvBuf Pointer to buffer for reading data.
8568 * @param cbRead Number of bytes to read.
8569 */
8570VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuf,
8571 size_t cbRead)
8572{
8573 int rc = VINF_SUCCESS;
8574 int rc2;
8575 bool fLockRead = false;
8576
8577 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbRead=%zu\n",
8578 pDisk, uOffset, pvBuf, cbRead));
8579 do
8580 {
8581 /* sanity check */
8582 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8583 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8584
8585 /* Check arguments. */
8586 AssertMsgBreakStmt(VALID_PTR(pvBuf),
8587 ("pvBuf=%#p\n", pvBuf),
8588 rc = VERR_INVALID_PARAMETER);
8589 AssertMsgBreakStmt(cbRead,
8590 ("cbRead=%zu\n", cbRead),
8591 rc = VERR_INVALID_PARAMETER);
8592
8593 rc2 = vdThreadStartRead(pDisk);
8594 AssertRC(rc2);
8595 fLockRead = true;
8596
8597 PVDIMAGE pImage = pDisk->pLast;
8598 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
8599
8600 if (uOffset + cbRead > pDisk->cbSize)
8601 {
8602 /* Floppy images might be smaller than the standard expected by
8603 the floppy controller code. So, we won't fail here. */
8604 AssertMsgBreakStmt(pDisk->enmType == VDTYPE_FLOPPY,
8605 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
8606 uOffset, cbRead, pDisk->cbSize),
8607 rc = VERR_EOF);
8608 memset(pvBuf, 0xf6, cbRead); /* f6h = format.com filler byte */
8609 if (uOffset >= pDisk->cbSize)
8610 break;
8611 cbRead = pDisk->cbSize - uOffset;
8612 }
8613
8614 rc = vdReadHelper(pDisk, pImage, uOffset, pvBuf, cbRead,
8615 true /* fUpdateCache */);
8616 } while (0);
8617
8618 if (RT_UNLIKELY(fLockRead))
8619 {
8620 rc2 = vdThreadFinishRead(pDisk);
8621 AssertRC(rc2);
8622 }
8623
8624 LogFlowFunc(("returns %Rrc\n", rc));
8625 return rc;
8626}
8627
8628/**
8629 * Write data to virtual HDD.
8630 *
8631 * @returns VBox status code.
8632 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
8633 * @param pDisk Pointer to HDD container.
8634 * @param uOffset Offset of the first byte being
8635 * written from start of disk.
8636 * @param pvBuf Pointer to buffer for writing data.
8637 * @param cbWrite Number of bytes to write.
8638 */
8639VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf,
8640 size_t cbWrite)
8641{
8642 int rc = VINF_SUCCESS;
8643 int rc2;
8644 bool fLockWrite = false;
8645
8646 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbWrite=%zu\n",
8647 pDisk, uOffset, pvBuf, cbWrite));
8648 do
8649 {
8650 /* sanity check */
8651 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8652 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8653
8654 /* Check arguments. */
8655 AssertMsgBreakStmt(VALID_PTR(pvBuf),
8656 ("pvBuf=%#p\n", pvBuf),
8657 rc = VERR_INVALID_PARAMETER);
8658 AssertMsgBreakStmt(cbWrite,
8659 ("cbWrite=%zu\n", cbWrite),
8660 rc = VERR_INVALID_PARAMETER);
8661
8662 rc2 = vdThreadStartWrite(pDisk);
8663 AssertRC(rc2);
8664 fLockWrite = true;
8665
8666 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
8667 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
8668 uOffset, cbWrite, pDisk->cbSize),
8669 rc = VERR_INVALID_PARAMETER);
8670
8671 PVDIMAGE pImage = pDisk->pLast;
8672 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
8673
8674 vdSetModifiedFlag(pDisk);
8675 rc = vdWriteHelper(pDisk, pImage, uOffset, pvBuf, cbWrite,
8676 VDIOCTX_FLAGS_READ_UPDATE_CACHE);
8677 if (RT_FAILURE(rc))
8678 break;
8679
8680 /* If there is a merge (in the direction towards a parent) running
8681 * concurrently then we have to also "relay" the write to this parent,
8682 * as the merge position might be already past the position where
8683 * this write is going. The "context" of the write can come from the
8684 * natural chain, since merging either already did or will take care
8685 * of the "other" content which is might be needed to fill the block
8686 * to a full allocation size. The cache doesn't need to be touched
8687 * as this write is covered by the previous one. */
8688 if (RT_UNLIKELY(pDisk->pImageRelay))
8689 rc = vdWriteHelper(pDisk, pDisk->pImageRelay, uOffset,
8690 pvBuf, cbWrite, VDIOCTX_FLAGS_DEFAULT);
8691 } while (0);
8692
8693 if (RT_UNLIKELY(fLockWrite))
8694 {
8695 rc2 = vdThreadFinishWrite(pDisk);
8696 AssertRC(rc2);
8697 }
8698
8699 LogFlowFunc(("returns %Rrc\n", rc));
8700 return rc;
8701}
8702
8703/**
8704 * Make sure the on disk representation of a virtual HDD is up to date.
8705 *
8706 * @returns VBox status code.
8707 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
8708 * @param pDisk Pointer to HDD container.
8709 */
8710VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
8711{
8712 int rc = VINF_SUCCESS;
8713 int rc2;
8714 bool fLockWrite = false;
8715
8716 LogFlowFunc(("pDisk=%#p\n", pDisk));
8717 do
8718 {
8719 /* sanity check */
8720 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8721 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8722
8723 rc2 = vdThreadStartWrite(pDisk);
8724 AssertRC(rc2);
8725 fLockWrite = true;
8726
8727 PVDIMAGE pImage = pDisk->pLast;
8728 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
8729
8730 VDIOCTX IoCtx;
8731 RTSEMEVENT hEventComplete = NIL_RTSEMEVENT;
8732
8733 rc = RTSemEventCreate(&hEventComplete);
8734 if (RT_FAILURE(rc))
8735 break;
8736
8737 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_FLUSH, 0, 0, pImage, NULL,
8738 NULL, vdFlushHelperAsync, VDIOCTX_FLAGS_SYNC | VDIOCTX_FLAGS_DONT_FREE);
8739
8740 IoCtx.Type.Root.pfnComplete = vdIoCtxSyncComplete;
8741 IoCtx.Type.Root.pvUser1 = pDisk;
8742 IoCtx.Type.Root.pvUser2 = hEventComplete;
8743 rc = vdIoCtxProcessSync(&IoCtx, hEventComplete);
8744
8745 RTSemEventDestroy(hEventComplete);
8746 } while (0);
8747
8748 if (RT_UNLIKELY(fLockWrite))
8749 {
8750 rc2 = vdThreadFinishWrite(pDisk);
8751 AssertRC(rc2);
8752 }
8753
8754 LogFlowFunc(("returns %Rrc\n", rc));
8755 return rc;
8756}
8757
8758/**
8759 * Get number of opened images in HDD container.
8760 *
8761 * @returns Number of opened images for HDD container. 0 if no images have been opened.
8762 * @param pDisk Pointer to HDD container.
8763 */
8764VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk)
8765{
8766 unsigned cImages;
8767 int rc2;
8768 bool fLockRead = false;
8769
8770 LogFlowFunc(("pDisk=%#p\n", pDisk));
8771 do
8772 {
8773 /* sanity check */
8774 AssertPtrBreakStmt(pDisk, cImages = 0);
8775 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8776
8777 rc2 = vdThreadStartRead(pDisk);
8778 AssertRC(rc2);
8779 fLockRead = true;
8780
8781 cImages = pDisk->cImages;
8782 } while (0);
8783
8784 if (RT_UNLIKELY(fLockRead))
8785 {
8786 rc2 = vdThreadFinishRead(pDisk);
8787 AssertRC(rc2);
8788 }
8789
8790 LogFlowFunc(("returns %u\n", cImages));
8791 return cImages;
8792}
8793
8794/**
8795 * Get read/write mode of HDD container.
8796 *
8797 * @returns Virtual disk ReadOnly status.
8798 * @returns true if no image is opened in HDD container.
8799 * @param pDisk Pointer to HDD container.
8800 */
8801VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
8802{
8803 bool fReadOnly;
8804 int rc2;
8805 bool fLockRead = false;
8806
8807 LogFlowFunc(("pDisk=%#p\n", pDisk));
8808 do
8809 {
8810 /* sanity check */
8811 AssertPtrBreakStmt(pDisk, fReadOnly = false);
8812 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8813
8814 rc2 = vdThreadStartRead(pDisk);
8815 AssertRC(rc2);
8816 fLockRead = true;
8817
8818 PVDIMAGE pImage = pDisk->pLast;
8819 AssertPtrBreakStmt(pImage, fReadOnly = true);
8820
8821 unsigned uOpenFlags;
8822 uOpenFlags = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
8823 fReadOnly = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
8824 } while (0);
8825
8826 if (RT_UNLIKELY(fLockRead))
8827 {
8828 rc2 = vdThreadFinishRead(pDisk);
8829 AssertRC(rc2);
8830 }
8831
8832 LogFlowFunc(("returns %d\n", fReadOnly));
8833 return fReadOnly;
8834}
8835
8836/**
8837 * Get sector size of an image in HDD container.
8838 *
8839 * @return Virtual disk sector size in bytes.
8840 * @return 0 if image with specified number was not opened.
8841 * @param pDisk Pointer to HDD container.
8842 * @param nImage Image number, counts from 0. 0 is always base image of container.
8843 */
8844VBOXDDU_DECL(uint32_t) VDGetSectorSize(PVBOXHDD pDisk, unsigned nImage)
8845{
8846 uint64_t cbSector;
8847 int rc2;
8848 bool fLockRead = false;
8849
8850 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
8851 do
8852 {
8853 /* sanity check */
8854 AssertPtrBreakStmt(pDisk, cbSector = 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 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8862 AssertPtrBreakStmt(pImage, cbSector = 0);
8863 cbSector = pImage->Backend->pfnGetSectorSize(pImage->pBackendData);
8864 } while (0);
8865
8866 if (RT_UNLIKELY(fLockRead))
8867 {
8868 rc2 = vdThreadFinishRead(pDisk);
8869 AssertRC(rc2);
8870 }
8871
8872 LogFlowFunc(("returns %u\n", cbSector));
8873 return cbSector;
8874}
8875
8876/**
8877 * Get total capacity of an image in HDD container.
8878 *
8879 * @returns Virtual disk size in bytes.
8880 * @returns 0 if no image with specified number was not opened.
8881 * @param pDisk Pointer to HDD container.
8882 * @param nImage Image number, counts from 0. 0 is always base image of container.
8883 */
8884VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk, unsigned nImage)
8885{
8886 uint64_t cbSize;
8887 int rc2;
8888 bool fLockRead = false;
8889
8890 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
8891 do
8892 {
8893 /* sanity check */
8894 AssertPtrBreakStmt(pDisk, cbSize = 0);
8895 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8896
8897 rc2 = vdThreadStartRead(pDisk);
8898 AssertRC(rc2);
8899 fLockRead = true;
8900
8901 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8902 AssertPtrBreakStmt(pImage, cbSize = 0);
8903 cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
8904 } while (0);
8905
8906 if (RT_UNLIKELY(fLockRead))
8907 {
8908 rc2 = vdThreadFinishRead(pDisk);
8909 AssertRC(rc2);
8910 }
8911
8912 LogFlowFunc(("returns %llu\n", cbSize));
8913 return cbSize;
8914}
8915
8916/**
8917 * Get total file size of an image in HDD container.
8918 *
8919 * @returns Virtual disk size in bytes.
8920 * @returns 0 if no image is opened in HDD container.
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(uint64_t) VDGetFileSize(PVBOXHDD pDisk, unsigned nImage)
8925{
8926 uint64_t cbSize;
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, cbSize = 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, cbSize = 0);
8943 cbSize = pImage->Backend->pfnGetFileSize(pImage->pBackendData);
8944 } while (0);
8945
8946 if (RT_UNLIKELY(fLockRead))
8947 {
8948 rc2 = vdThreadFinishRead(pDisk);
8949 AssertRC(rc2);
8950 }
8951
8952 LogFlowFunc(("returns %llu\n", cbSize));
8953 return cbSize;
8954}
8955
8956/**
8957 * Get virtual disk PCHS geometry stored in HDD container.
8958 *
8959 * @returns VBox status code.
8960 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
8961 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
8962 * @param pDisk Pointer to HDD container.
8963 * @param nImage Image number, counts from 0. 0 is always base image of container.
8964 * @param pPCHSGeometry Where to store PCHS geometry. Not NULL.
8965 */
8966VBOXDDU_DECL(int) VDGetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
8967 PVDGEOMETRY pPCHSGeometry)
8968{
8969 int rc = VINF_SUCCESS;
8970 int rc2;
8971 bool fLockRead = false;
8972
8973 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p\n",
8974 pDisk, nImage, pPCHSGeometry));
8975 do
8976 {
8977 /* sanity check */
8978 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8979 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8980
8981 /* Check arguments. */
8982 AssertMsgBreakStmt(VALID_PTR(pPCHSGeometry),
8983 ("pPCHSGeometry=%#p\n", pPCHSGeometry),
8984 rc = VERR_INVALID_PARAMETER);
8985
8986 rc2 = vdThreadStartRead(pDisk);
8987 AssertRC(rc2);
8988 fLockRead = true;
8989
8990 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8991 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
8992
8993 if (pImage == pDisk->pLast)
8994 {
8995 /* Use cached information if possible. */
8996 if (pDisk->PCHSGeometry.cCylinders != 0)
8997 *pPCHSGeometry = pDisk->PCHSGeometry;
8998 else
8999 rc = VERR_VD_GEOMETRY_NOT_SET;
9000 }
9001 else
9002 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
9003 pPCHSGeometry);
9004 } while (0);
9005
9006 if (RT_UNLIKELY(fLockRead))
9007 {
9008 rc2 = vdThreadFinishRead(pDisk);
9009 AssertRC(rc2);
9010 }
9011
9012 LogFlowFunc(("%Rrc (PCHS=%u/%u/%u)\n", rc,
9013 pDisk->PCHSGeometry.cCylinders, pDisk->PCHSGeometry.cHeads,
9014 pDisk->PCHSGeometry.cSectors));
9015 return rc;
9016}
9017
9018/**
9019 * Store virtual disk PCHS geometry in HDD container.
9020 *
9021 * Note that in case of unrecoverable error all images in HDD container will be closed.
9022 *
9023 * @returns VBox status code.
9024 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9025 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
9026 * @param pDisk Pointer to HDD container.
9027 * @param nImage Image number, counts from 0. 0 is always base image of container.
9028 * @param pPCHSGeometry Where to load PCHS geometry from. Not NULL.
9029 */
9030VBOXDDU_DECL(int) VDSetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
9031 PCVDGEOMETRY pPCHSGeometry)
9032{
9033 int rc = VINF_SUCCESS;
9034 int rc2;
9035 bool fLockWrite = false;
9036
9037 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
9038 pDisk, nImage, pPCHSGeometry, pPCHSGeometry->cCylinders,
9039 pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
9040 do
9041 {
9042 /* sanity check */
9043 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9044 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9045
9046 /* Check arguments. */
9047 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
9048 && pPCHSGeometry->cHeads <= 16
9049 && pPCHSGeometry->cSectors <= 63,
9050 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
9051 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
9052 pPCHSGeometry->cSectors),
9053 rc = VERR_INVALID_PARAMETER);
9054
9055 rc2 = vdThreadStartWrite(pDisk);
9056 AssertRC(rc2);
9057 fLockWrite = true;
9058
9059 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9060 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9061
9062 if (pImage == pDisk->pLast)
9063 {
9064 if ( pPCHSGeometry->cCylinders != pDisk->PCHSGeometry.cCylinders
9065 || pPCHSGeometry->cHeads != pDisk->PCHSGeometry.cHeads
9066 || pPCHSGeometry->cSectors != pDisk->PCHSGeometry.cSectors)
9067 {
9068 /* Only update geometry if it is changed. Avoids similar checks
9069 * in every backend. Most of the time the new geometry is set
9070 * to the previous values, so no need to go through the hassle
9071 * of updating an image which could be opened in read-only mode
9072 * right now. */
9073 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pBackendData,
9074 pPCHSGeometry);
9075
9076 /* Cache new geometry values in any case. */
9077 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
9078 &pDisk->PCHSGeometry);
9079 if (RT_FAILURE(rc2))
9080 {
9081 pDisk->PCHSGeometry.cCylinders = 0;
9082 pDisk->PCHSGeometry.cHeads = 0;
9083 pDisk->PCHSGeometry.cSectors = 0;
9084 }
9085 else
9086 {
9087 /* Make sure the CHS geometry is properly clipped. */
9088 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 255);
9089 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
9090 }
9091 }
9092 }
9093 else
9094 {
9095 VDGEOMETRY PCHS;
9096 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
9097 &PCHS);
9098 if ( RT_FAILURE(rc)
9099 || pPCHSGeometry->cCylinders != PCHS.cCylinders
9100 || pPCHSGeometry->cHeads != PCHS.cHeads
9101 || pPCHSGeometry->cSectors != PCHS.cSectors)
9102 {
9103 /* Only update geometry if it is changed. Avoids similar checks
9104 * in every backend. Most of the time the new geometry is set
9105 * to the previous values, so no need to go through the hassle
9106 * of updating an image which could be opened in read-only mode
9107 * right now. */
9108 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pBackendData,
9109 pPCHSGeometry);
9110 }
9111 }
9112 } while (0);
9113
9114 if (RT_UNLIKELY(fLockWrite))
9115 {
9116 rc2 = vdThreadFinishWrite(pDisk);
9117 AssertRC(rc2);
9118 }
9119
9120 LogFlowFunc(("returns %Rrc\n", rc));
9121 return rc;
9122}
9123
9124/**
9125 * Get virtual disk LCHS geometry stored in HDD container.
9126 *
9127 * @returns VBox status code.
9128 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9129 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
9130 * @param pDisk Pointer to HDD container.
9131 * @param nImage Image number, counts from 0. 0 is always base image of container.
9132 * @param pLCHSGeometry Where to store LCHS geometry. Not NULL.
9133 */
9134VBOXDDU_DECL(int) VDGetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
9135 PVDGEOMETRY pLCHSGeometry)
9136{
9137 int rc = VINF_SUCCESS;
9138 int rc2;
9139 bool fLockRead = false;
9140
9141 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p\n",
9142 pDisk, nImage, pLCHSGeometry));
9143 do
9144 {
9145 /* sanity check */
9146 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9147 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9148
9149 /* Check arguments. */
9150 AssertMsgBreakStmt(VALID_PTR(pLCHSGeometry),
9151 ("pLCHSGeometry=%#p\n", pLCHSGeometry),
9152 rc = VERR_INVALID_PARAMETER);
9153
9154 rc2 = vdThreadStartRead(pDisk);
9155 AssertRC(rc2);
9156 fLockRead = true;
9157
9158 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9159 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9160
9161 if (pImage == pDisk->pLast)
9162 {
9163 /* Use cached information if possible. */
9164 if (pDisk->LCHSGeometry.cCylinders != 0)
9165 *pLCHSGeometry = pDisk->LCHSGeometry;
9166 else
9167 rc = VERR_VD_GEOMETRY_NOT_SET;
9168 }
9169 else
9170 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
9171 pLCHSGeometry);
9172 } while (0);
9173
9174 if (RT_UNLIKELY(fLockRead))
9175 {
9176 rc2 = vdThreadFinishRead(pDisk);
9177 AssertRC(rc2);
9178 }
9179
9180 LogFlowFunc((": %Rrc (LCHS=%u/%u/%u)\n", rc,
9181 pDisk->LCHSGeometry.cCylinders, pDisk->LCHSGeometry.cHeads,
9182 pDisk->LCHSGeometry.cSectors));
9183 return rc;
9184}
9185
9186/**
9187 * Store virtual disk LCHS geometry in HDD container.
9188 *
9189 * Note that in case of unrecoverable error all images in HDD container will be closed.
9190 *
9191 * @returns VBox status code.
9192 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9193 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
9194 * @param pDisk Pointer to HDD container.
9195 * @param nImage Image number, counts from 0. 0 is always base image of container.
9196 * @param pLCHSGeometry Where to load LCHS geometry from. Not NULL.
9197 */
9198VBOXDDU_DECL(int) VDSetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
9199 PCVDGEOMETRY pLCHSGeometry)
9200{
9201 int rc = VINF_SUCCESS;
9202 int rc2;
9203 bool fLockWrite = false;
9204
9205 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
9206 pDisk, nImage, pLCHSGeometry, pLCHSGeometry->cCylinders,
9207 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
9208 do
9209 {
9210 /* sanity check */
9211 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9212 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9213
9214 /* Check arguments. */
9215 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
9216 && pLCHSGeometry->cHeads <= 255
9217 && pLCHSGeometry->cSectors <= 63,
9218 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
9219 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
9220 pLCHSGeometry->cSectors),
9221 rc = VERR_INVALID_PARAMETER);
9222
9223 rc2 = vdThreadStartWrite(pDisk);
9224 AssertRC(rc2);
9225 fLockWrite = true;
9226
9227 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9228 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9229
9230 if (pImage == pDisk->pLast)
9231 {
9232 if ( pLCHSGeometry->cCylinders != pDisk->LCHSGeometry.cCylinders
9233 || pLCHSGeometry->cHeads != pDisk->LCHSGeometry.cHeads
9234 || pLCHSGeometry->cSectors != pDisk->LCHSGeometry.cSectors)
9235 {
9236 /* Only update geometry if it is changed. Avoids similar checks
9237 * in every backend. Most of the time the new geometry is set
9238 * to the previous values, so no need to go through the hassle
9239 * of updating an image which could be opened in read-only mode
9240 * right now. */
9241 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pBackendData,
9242 pLCHSGeometry);
9243
9244 /* Cache new geometry values in any case. */
9245 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
9246 &pDisk->LCHSGeometry);
9247 if (RT_FAILURE(rc2))
9248 {
9249 pDisk->LCHSGeometry.cCylinders = 0;
9250 pDisk->LCHSGeometry.cHeads = 0;
9251 pDisk->LCHSGeometry.cSectors = 0;
9252 }
9253 else
9254 {
9255 /* Make sure the CHS geometry is properly clipped. */
9256 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
9257 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
9258 }
9259 }
9260 }
9261 else
9262 {
9263 VDGEOMETRY LCHS;
9264 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
9265 &LCHS);
9266 if ( RT_FAILURE(rc)
9267 || pLCHSGeometry->cCylinders != LCHS.cCylinders
9268 || pLCHSGeometry->cHeads != LCHS.cHeads
9269 || pLCHSGeometry->cSectors != LCHS.cSectors)
9270 {
9271 /* Only update geometry if it is changed. Avoids similar checks
9272 * in every backend. Most of the time the new geometry is set
9273 * to the previous values, so no need to go through the hassle
9274 * of updating an image which could be opened in read-only mode
9275 * right now. */
9276 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pBackendData,
9277 pLCHSGeometry);
9278 }
9279 }
9280 } while (0);
9281
9282 if (RT_UNLIKELY(fLockWrite))
9283 {
9284 rc2 = vdThreadFinishWrite(pDisk);
9285 AssertRC(rc2);
9286 }
9287
9288 LogFlowFunc(("returns %Rrc\n", rc));
9289 return rc;
9290}
9291
9292/**
9293 * Get version of image in HDD container.
9294 *
9295 * @returns VBox status code.
9296 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9297 * @param pDisk Pointer to HDD container.
9298 * @param nImage Image number, counts from 0. 0 is always base image of container.
9299 * @param puVersion Where to store the image version.
9300 */
9301VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
9302 unsigned *puVersion)
9303{
9304 int rc = VINF_SUCCESS;
9305 int rc2;
9306 bool fLockRead = false;
9307
9308 LogFlowFunc(("pDisk=%#p nImage=%u puVersion=%#p\n",
9309 pDisk, nImage, puVersion));
9310 do
9311 {
9312 /* sanity check */
9313 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9314 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9315
9316 /* Check arguments. */
9317 AssertMsgBreakStmt(VALID_PTR(puVersion),
9318 ("puVersion=%#p\n", puVersion),
9319 rc = VERR_INVALID_PARAMETER);
9320
9321 rc2 = vdThreadStartRead(pDisk);
9322 AssertRC(rc2);
9323 fLockRead = true;
9324
9325 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9326 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9327
9328 *puVersion = pImage->Backend->pfnGetVersion(pImage->pBackendData);
9329 } while (0);
9330
9331 if (RT_UNLIKELY(fLockRead))
9332 {
9333 rc2 = vdThreadFinishRead(pDisk);
9334 AssertRC(rc2);
9335 }
9336
9337 LogFlowFunc(("returns %Rrc uVersion=%#x\n", rc, *puVersion));
9338 return rc;
9339}
9340
9341/**
9342 * List the capabilities of image backend in HDD container.
9343 *
9344 * @returns VBox status code.
9345 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9346 * @param pDisk Pointer to the HDD container.
9347 * @param nImage Image number, counts from 0. 0 is always base image of container.
9348 * @param pbackendInfo Where to store the backend information.
9349 */
9350VBOXDDU_DECL(int) VDBackendInfoSingle(PVBOXHDD pDisk, unsigned nImage,
9351 PVDBACKENDINFO pBackendInfo)
9352{
9353 int rc = VINF_SUCCESS;
9354 int rc2;
9355 bool fLockRead = false;
9356
9357 LogFlowFunc(("pDisk=%#p nImage=%u pBackendInfo=%#p\n",
9358 pDisk, nImage, pBackendInfo));
9359 do
9360 {
9361 /* sanity check */
9362 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9363 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9364
9365 /* Check arguments. */
9366 AssertMsgBreakStmt(VALID_PTR(pBackendInfo),
9367 ("pBackendInfo=%#p\n", pBackendInfo),
9368 rc = VERR_INVALID_PARAMETER);
9369
9370 rc2 = vdThreadStartRead(pDisk);
9371 AssertRC(rc2);
9372 fLockRead = true;
9373
9374 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9375 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9376
9377 pBackendInfo->pszBackend = pImage->Backend->pszBackendName;
9378 pBackendInfo->uBackendCaps = pImage->Backend->uBackendCaps;
9379 pBackendInfo->paFileExtensions = pImage->Backend->paFileExtensions;
9380 pBackendInfo->paConfigInfo = pImage->Backend->paConfigInfo;
9381 } while (0);
9382
9383 if (RT_UNLIKELY(fLockRead))
9384 {
9385 rc2 = vdThreadFinishRead(pDisk);
9386 AssertRC(rc2);
9387 }
9388
9389 LogFlowFunc(("returns %Rrc\n", rc));
9390 return rc;
9391}
9392
9393/**
9394 * Get flags of image in HDD container.
9395 *
9396 * @returns VBox status code.
9397 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9398 * @param pDisk Pointer to HDD container.
9399 * @param nImage Image number, counts from 0. 0 is always base image of container.
9400 * @param puImageFlags Where to store the image flags.
9401 */
9402VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage,
9403 unsigned *puImageFlags)
9404{
9405 int rc = VINF_SUCCESS;
9406 int rc2;
9407 bool fLockRead = false;
9408
9409 LogFlowFunc(("pDisk=%#p nImage=%u puImageFlags=%#p\n",
9410 pDisk, nImage, puImageFlags));
9411 do
9412 {
9413 /* sanity check */
9414 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9415 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9416
9417 /* Check arguments. */
9418 AssertMsgBreakStmt(VALID_PTR(puImageFlags),
9419 ("puImageFlags=%#p\n", puImageFlags),
9420 rc = VERR_INVALID_PARAMETER);
9421
9422 rc2 = vdThreadStartRead(pDisk);
9423 AssertRC(rc2);
9424 fLockRead = true;
9425
9426 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9427 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9428
9429 *puImageFlags = pImage->uImageFlags;
9430 } while (0);
9431
9432 if (RT_UNLIKELY(fLockRead))
9433 {
9434 rc2 = vdThreadFinishRead(pDisk);
9435 AssertRC(rc2);
9436 }
9437
9438 LogFlowFunc(("returns %Rrc uImageFlags=%#x\n", rc, *puImageFlags));
9439 return rc;
9440}
9441
9442/**
9443 * Get open flags of image in HDD container.
9444 *
9445 * @returns VBox status code.
9446 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9447 * @param pDisk Pointer to HDD container.
9448 * @param nImage Image number, counts from 0. 0 is always base image of container.
9449 * @param puOpenFlags Where to store the image open flags.
9450 */
9451VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
9452 unsigned *puOpenFlags)
9453{
9454 int rc = VINF_SUCCESS;
9455 int rc2;
9456 bool fLockRead = false;
9457
9458 LogFlowFunc(("pDisk=%#p nImage=%u puOpenFlags=%#p\n",
9459 pDisk, nImage, puOpenFlags));
9460 do
9461 {
9462 /* sanity check */
9463 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9464 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9465
9466 /* Check arguments. */
9467 AssertMsgBreakStmt(VALID_PTR(puOpenFlags),
9468 ("puOpenFlags=%#p\n", puOpenFlags),
9469 rc = VERR_INVALID_PARAMETER);
9470
9471 rc2 = vdThreadStartRead(pDisk);
9472 AssertRC(rc2);
9473 fLockRead = true;
9474
9475 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9476 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9477
9478 *puOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
9479 } while (0);
9480
9481 if (RT_UNLIKELY(fLockRead))
9482 {
9483 rc2 = vdThreadFinishRead(pDisk);
9484 AssertRC(rc2);
9485 }
9486
9487 LogFlowFunc(("returns %Rrc uOpenFlags=%#x\n", rc, *puOpenFlags));
9488 return rc;
9489}
9490
9491/**
9492 * Set open flags of image in HDD container.
9493 * This operation may cause file locking changes and/or files being reopened.
9494 * Note that in case of unrecoverable error all images in HDD container will be closed.
9495 *
9496 * @returns VBox status code.
9497 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9498 * @param pDisk Pointer to HDD container.
9499 * @param nImage Image number, counts from 0. 0 is always base image of container.
9500 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
9501 */
9502VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
9503 unsigned uOpenFlags)
9504{
9505 int rc;
9506 int rc2;
9507 bool fLockWrite = false;
9508
9509 LogFlowFunc(("pDisk=%#p uOpenFlags=%#u\n", pDisk, uOpenFlags));
9510 do
9511 {
9512 /* sanity check */
9513 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9514 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9515
9516 /* Check arguments. */
9517 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
9518 ("uOpenFlags=%#x\n", uOpenFlags),
9519 rc = VERR_INVALID_PARAMETER);
9520
9521 rc2 = vdThreadStartWrite(pDisk);
9522 AssertRC(rc2);
9523 fLockWrite = true;
9524
9525 /* Destroy any discard state because the image might be changed to readonly mode. */
9526 rc = vdDiscardStateDestroy(pDisk);
9527 if (RT_FAILURE(rc))
9528 break;
9529
9530 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9531 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9532
9533 rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData,
9534 uOpenFlags & ~(VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_IGNORE_FLUSH | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS));
9535 if (RT_SUCCESS(rc))
9536 pImage->uOpenFlags = uOpenFlags & (VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_DISCARD | VD_OPEN_FLAGS_IGNORE_FLUSH | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS);
9537 } while (0);
9538
9539 if (RT_UNLIKELY(fLockWrite))
9540 {
9541 rc2 = vdThreadFinishWrite(pDisk);
9542 AssertRC(rc2);
9543 }
9544
9545 LogFlowFunc(("returns %Rrc\n", rc));
9546 return rc;
9547}
9548
9549/**
9550 * Get base filename of image in HDD container. Some image formats use
9551 * other filenames as well, so don't use this for anything but informational
9552 * purposes.
9553 *
9554 * @returns VBox status code.
9555 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9556 * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
9557 * @param pDisk Pointer to HDD container.
9558 * @param nImage Image number, counts from 0. 0 is always base image of container.
9559 * @param pszFilename Where to store the image file name.
9560 * @param cbFilename Size of buffer pszFilename points to.
9561 */
9562VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
9563 char *pszFilename, unsigned cbFilename)
9564{
9565 int rc;
9566 int rc2;
9567 bool fLockRead = false;
9568
9569 LogFlowFunc(("pDisk=%#p nImage=%u pszFilename=%#p cbFilename=%u\n",
9570 pDisk, nImage, pszFilename, cbFilename));
9571 do
9572 {
9573 /* sanity check */
9574 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9575 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9576
9577 /* Check arguments. */
9578 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
9579 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
9580 rc = VERR_INVALID_PARAMETER);
9581 AssertMsgBreakStmt(cbFilename,
9582 ("cbFilename=%u\n", cbFilename),
9583 rc = VERR_INVALID_PARAMETER);
9584
9585 rc2 = vdThreadStartRead(pDisk);
9586 AssertRC(rc2);
9587 fLockRead = true;
9588
9589 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9590 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9591
9592 size_t cb = strlen(pImage->pszFilename);
9593 if (cb <= cbFilename)
9594 {
9595 strcpy(pszFilename, pImage->pszFilename);
9596 rc = VINF_SUCCESS;
9597 }
9598 else
9599 {
9600 strncpy(pszFilename, pImage->pszFilename, cbFilename - 1);
9601 pszFilename[cbFilename - 1] = '\0';
9602 rc = VERR_BUFFER_OVERFLOW;
9603 }
9604 } while (0);
9605
9606 if (RT_UNLIKELY(fLockRead))
9607 {
9608 rc2 = vdThreadFinishRead(pDisk);
9609 AssertRC(rc2);
9610 }
9611
9612 LogFlowFunc(("returns %Rrc, pszFilename=\"%s\"\n", rc, pszFilename));
9613 return rc;
9614}
9615
9616/**
9617 * Get the comment line of image in HDD container.
9618 *
9619 * @returns VBox status code.
9620 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9621 * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
9622 * @param pDisk Pointer to HDD container.
9623 * @param nImage Image number, counts from 0. 0 is always base image of container.
9624 * @param pszComment Where to store the comment string of image. NULL is ok.
9625 * @param cbComment The size of pszComment buffer. 0 is ok.
9626 */
9627VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
9628 char *pszComment, unsigned cbComment)
9629{
9630 int rc;
9631 int rc2;
9632 bool fLockRead = false;
9633
9634 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p cbComment=%u\n",
9635 pDisk, nImage, pszComment, cbComment));
9636 do
9637 {
9638 /* sanity check */
9639 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9640 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9641
9642 /* Check arguments. */
9643 AssertMsgBreakStmt(VALID_PTR(pszComment),
9644 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
9645 rc = VERR_INVALID_PARAMETER);
9646 AssertMsgBreakStmt(cbComment,
9647 ("cbComment=%u\n", cbComment),
9648 rc = VERR_INVALID_PARAMETER);
9649
9650 rc2 = vdThreadStartRead(pDisk);
9651 AssertRC(rc2);
9652 fLockRead = true;
9653
9654 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9655 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9656
9657 rc = pImage->Backend->pfnGetComment(pImage->pBackendData, pszComment,
9658 cbComment);
9659 } while (0);
9660
9661 if (RT_UNLIKELY(fLockRead))
9662 {
9663 rc2 = vdThreadFinishRead(pDisk);
9664 AssertRC(rc2);
9665 }
9666
9667 LogFlowFunc(("returns %Rrc, pszComment=\"%s\"\n", rc, pszComment));
9668 return rc;
9669}
9670
9671/**
9672 * Changes the comment line of image in HDD container.
9673 *
9674 * @returns VBox status code.
9675 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9676 * @param pDisk Pointer to HDD container.
9677 * @param nImage Image number, counts from 0. 0 is always base image of container.
9678 * @param pszComment New comment string (UTF-8). NULL is allowed to reset the comment.
9679 */
9680VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
9681 const char *pszComment)
9682{
9683 int rc;
9684 int rc2;
9685 bool fLockWrite = false;
9686
9687 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p \"%s\"\n",
9688 pDisk, nImage, pszComment, pszComment));
9689 do
9690 {
9691 /* sanity check */
9692 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9693 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9694
9695 /* Check arguments. */
9696 AssertMsgBreakStmt(VALID_PTR(pszComment) || pszComment == NULL,
9697 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
9698 rc = VERR_INVALID_PARAMETER);
9699
9700 rc2 = vdThreadStartWrite(pDisk);
9701 AssertRC(rc2);
9702 fLockWrite = true;
9703
9704 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9705 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9706
9707 rc = pImage->Backend->pfnSetComment(pImage->pBackendData, pszComment);
9708 } while (0);
9709
9710 if (RT_UNLIKELY(fLockWrite))
9711 {
9712 rc2 = vdThreadFinishWrite(pDisk);
9713 AssertRC(rc2);
9714 }
9715
9716 LogFlowFunc(("returns %Rrc\n", rc));
9717 return rc;
9718}
9719
9720
9721/**
9722 * Get UUID of image in HDD container.
9723 *
9724 * @returns VBox status code.
9725 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9726 * @param pDisk Pointer to HDD container.
9727 * @param nImage Image number, counts from 0. 0 is always base image of container.
9728 * @param pUuid Where to store the image creation UUID.
9729 */
9730VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
9731{
9732 int rc;
9733 int rc2;
9734 bool fLockRead = false;
9735
9736 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
9737 do
9738 {
9739 /* sanity check */
9740 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9741 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9742
9743 /* Check arguments. */
9744 AssertMsgBreakStmt(VALID_PTR(pUuid),
9745 ("pUuid=%#p\n", pUuid),
9746 rc = VERR_INVALID_PARAMETER);
9747
9748 rc2 = vdThreadStartRead(pDisk);
9749 AssertRC(rc2);
9750 fLockRead = true;
9751
9752 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9753 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9754
9755 rc = pImage->Backend->pfnGetUuid(pImage->pBackendData, pUuid);
9756 } while (0);
9757
9758 if (RT_UNLIKELY(fLockRead))
9759 {
9760 rc2 = vdThreadFinishRead(pDisk);
9761 AssertRC(rc2);
9762 }
9763
9764 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
9765 return rc;
9766}
9767
9768/**
9769 * Set the image's UUID. Should not be used by normal applications.
9770 *
9771 * @returns VBox status code.
9772 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9773 * @param pDisk Pointer to HDD container.
9774 * @param nImage Image number, counts from 0. 0 is always base image of container.
9775 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
9776 */
9777VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
9778{
9779 int rc;
9780 int rc2;
9781 bool fLockWrite = false;
9782
9783 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
9784 pDisk, nImage, pUuid, pUuid));
9785 do
9786 {
9787 /* sanity check */
9788 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9789 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9790
9791 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
9792 ("pUuid=%#p\n", pUuid),
9793 rc = VERR_INVALID_PARAMETER);
9794
9795 rc2 = vdThreadStartWrite(pDisk);
9796 AssertRC(rc2);
9797 fLockWrite = true;
9798
9799 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9800 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9801
9802 RTUUID Uuid;
9803 if (!pUuid)
9804 {
9805 RTUuidCreate(&Uuid);
9806 pUuid = &Uuid;
9807 }
9808 rc = pImage->Backend->pfnSetUuid(pImage->pBackendData, pUuid);
9809 } while (0);
9810
9811 if (RT_UNLIKELY(fLockWrite))
9812 {
9813 rc2 = vdThreadFinishWrite(pDisk);
9814 AssertRC(rc2);
9815 }
9816
9817 LogFlowFunc(("returns %Rrc\n", rc));
9818 return rc;
9819}
9820
9821/**
9822 * Get last modification UUID of image in HDD container.
9823 *
9824 * @returns VBox status code.
9825 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9826 * @param pDisk Pointer to HDD container.
9827 * @param nImage Image number, counts from 0. 0 is always base image of container.
9828 * @param pUuid Where to store the image modification UUID.
9829 */
9830VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
9831{
9832 int rc = VINF_SUCCESS;
9833 int rc2;
9834 bool fLockRead = false;
9835
9836 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
9837 do
9838 {
9839 /* sanity check */
9840 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9841 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9842
9843 /* Check arguments. */
9844 AssertMsgBreakStmt(VALID_PTR(pUuid),
9845 ("pUuid=%#p\n", pUuid),
9846 rc = VERR_INVALID_PARAMETER);
9847
9848 rc2 = vdThreadStartRead(pDisk);
9849 AssertRC(rc2);
9850 fLockRead = true;
9851
9852 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9853 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9854
9855 rc = pImage->Backend->pfnGetModificationUuid(pImage->pBackendData,
9856 pUuid);
9857 } while (0);
9858
9859 if (RT_UNLIKELY(fLockRead))
9860 {
9861 rc2 = vdThreadFinishRead(pDisk);
9862 AssertRC(rc2);
9863 }
9864
9865 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
9866 return rc;
9867}
9868
9869/**
9870 * Set the image's last modification UUID. Should not be used by normal applications.
9871 *
9872 * @returns VBox status code.
9873 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9874 * @param pDisk Pointer to HDD container.
9875 * @param nImage Image number, counts from 0. 0 is always base image of container.
9876 * @param pUuid New modification UUID of the image. If NULL, a new UUID is created.
9877 */
9878VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
9879{
9880 int rc;
9881 int rc2;
9882 bool fLockWrite = false;
9883
9884 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
9885 pDisk, nImage, pUuid, pUuid));
9886 do
9887 {
9888 /* sanity check */
9889 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9890 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9891
9892 /* Check arguments. */
9893 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
9894 ("pUuid=%#p\n", pUuid),
9895 rc = VERR_INVALID_PARAMETER);
9896
9897 rc2 = vdThreadStartWrite(pDisk);
9898 AssertRC(rc2);
9899 fLockWrite = true;
9900
9901 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9902 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9903
9904 RTUUID Uuid;
9905 if (!pUuid)
9906 {
9907 RTUuidCreate(&Uuid);
9908 pUuid = &Uuid;
9909 }
9910 rc = pImage->Backend->pfnSetModificationUuid(pImage->pBackendData,
9911 pUuid);
9912 } while (0);
9913
9914 if (RT_UNLIKELY(fLockWrite))
9915 {
9916 rc2 = vdThreadFinishWrite(pDisk);
9917 AssertRC(rc2);
9918 }
9919
9920 LogFlowFunc(("returns %Rrc\n", rc));
9921 return rc;
9922}
9923
9924/**
9925 * Get parent UUID of image in HDD container.
9926 *
9927 * @returns VBox status code.
9928 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9929 * @param pDisk Pointer to HDD container.
9930 * @param nImage Image number, counts from 0. 0 is always base image of container.
9931 * @param pUuid Where to store the parent image UUID.
9932 */
9933VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
9934 PRTUUID pUuid)
9935{
9936 int rc = VINF_SUCCESS;
9937 int rc2;
9938 bool fLockRead = false;
9939
9940 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
9941 do
9942 {
9943 /* sanity check */
9944 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9945 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9946
9947 /* Check arguments. */
9948 AssertMsgBreakStmt(VALID_PTR(pUuid),
9949 ("pUuid=%#p\n", pUuid),
9950 rc = VERR_INVALID_PARAMETER);
9951
9952 rc2 = vdThreadStartRead(pDisk);
9953 AssertRC(rc2);
9954 fLockRead = true;
9955
9956 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9957 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9958
9959 rc = pImage->Backend->pfnGetParentUuid(pImage->pBackendData, pUuid);
9960 } while (0);
9961
9962 if (RT_UNLIKELY(fLockRead))
9963 {
9964 rc2 = vdThreadFinishRead(pDisk);
9965 AssertRC(rc2);
9966 }
9967
9968 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
9969 return rc;
9970}
9971
9972/**
9973 * Set the image's parent UUID. Should not be used by normal applications.
9974 *
9975 * @returns VBox status code.
9976 * @param pDisk Pointer to HDD container.
9977 * @param nImage Image number, counts from 0. 0 is always base image of container.
9978 * @param pUuid New parent UUID of the image. If NULL, a new UUID is created.
9979 */
9980VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
9981 PCRTUUID pUuid)
9982{
9983 int rc;
9984 int rc2;
9985 bool fLockWrite = false;
9986
9987 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
9988 pDisk, nImage, pUuid, pUuid));
9989 do
9990 {
9991 /* sanity check */
9992 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9993 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9994
9995 /* Check arguments. */
9996 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
9997 ("pUuid=%#p\n", pUuid),
9998 rc = VERR_INVALID_PARAMETER);
9999
10000 rc2 = vdThreadStartWrite(pDisk);
10001 AssertRC(rc2);
10002 fLockWrite = true;
10003
10004 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
10005 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
10006
10007 RTUUID Uuid;
10008 if (!pUuid)
10009 {
10010 RTUuidCreate(&Uuid);
10011 pUuid = &Uuid;
10012 }
10013 rc = pImage->Backend->pfnSetParentUuid(pImage->pBackendData, pUuid);
10014 } while (0);
10015
10016 if (RT_UNLIKELY(fLockWrite))
10017 {
10018 rc2 = vdThreadFinishWrite(pDisk);
10019 AssertRC(rc2);
10020 }
10021
10022 LogFlowFunc(("returns %Rrc\n", rc));
10023 return rc;
10024}
10025
10026
10027/**
10028 * Debug helper - dumps all opened images in HDD container into the log file.
10029 *
10030 * @param pDisk Pointer to HDD container.
10031 */
10032VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk)
10033{
10034 int rc2;
10035 bool fLockRead = false;
10036
10037 do
10038 {
10039 /* sanity check */
10040 AssertPtrBreak(pDisk);
10041 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10042
10043 if (!pDisk->pInterfaceError || !VALID_PTR(pDisk->pInterfaceError->pfnMessage))
10044 pDisk->pInterfaceError->pfnMessage = vdLogMessage;
10045
10046 rc2 = vdThreadStartRead(pDisk);
10047 AssertRC(rc2);
10048 fLockRead = true;
10049
10050 vdMessageWrapper(pDisk, "--- Dumping VD Disk, Images=%u\n", pDisk->cImages);
10051 for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
10052 {
10053 vdMessageWrapper(pDisk, "Dumping VD image \"%s\" (Backend=%s)\n",
10054 pImage->pszFilename, pImage->Backend->pszBackendName);
10055 pImage->Backend->pfnDump(pImage->pBackendData);
10056 }
10057 } while (0);
10058
10059 if (RT_UNLIKELY(fLockRead))
10060 {
10061 rc2 = vdThreadFinishRead(pDisk);
10062 AssertRC(rc2);
10063 }
10064}
10065
10066
10067VBOXDDU_DECL(int) VDDiscardRanges(PVBOXHDD pDisk, PCRTRANGE paRanges, unsigned cRanges)
10068{
10069 int rc;
10070 int rc2;
10071 bool fLockWrite = false;
10072
10073 LogFlowFunc(("pDisk=%#p paRanges=%#p cRanges=%u\n",
10074 pDisk, paRanges, cRanges));
10075 do
10076 {
10077 /* sanity check */
10078 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10079 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10080
10081 /* Check arguments. */
10082 AssertMsgBreakStmt(cRanges,
10083 ("cRanges=%u\n", cRanges),
10084 rc = VERR_INVALID_PARAMETER);
10085 AssertMsgBreakStmt(VALID_PTR(paRanges),
10086 ("paRanges=%#p\n", paRanges),
10087 rc = VERR_INVALID_PARAMETER);
10088
10089 rc2 = vdThreadStartWrite(pDisk);
10090 AssertRC(rc2);
10091 fLockWrite = true;
10092
10093 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
10094
10095 AssertMsgBreakStmt(pDisk->pLast->uOpenFlags & VD_OPEN_FLAGS_DISCARD,
10096 ("Discarding not supported\n"),
10097 rc = VERR_NOT_SUPPORTED);
10098
10099 VDIOCTX IoCtx;
10100 RTSEMEVENT hEventComplete = NIL_RTSEMEVENT;
10101
10102 rc = RTSemEventCreate(&hEventComplete);
10103 if (RT_FAILURE(rc))
10104 break;
10105
10106 vdIoCtxDiscardInit(&IoCtx, pDisk, paRanges, cRanges,
10107 vdIoCtxSyncComplete, pDisk, hEventComplete, NULL,
10108 vdDiscardHelperAsync, VDIOCTX_FLAGS_SYNC | VDIOCTX_FLAGS_DONT_FREE);
10109 rc = vdIoCtxProcessSync(&IoCtx, hEventComplete);
10110
10111 RTSemEventDestroy(hEventComplete);
10112 } while (0);
10113
10114 if (RT_UNLIKELY(fLockWrite))
10115 {
10116 rc2 = vdThreadFinishWrite(pDisk);
10117 AssertRC(rc2);
10118 }
10119
10120 LogFlowFunc(("returns %Rrc\n", rc));
10121 return rc;
10122}
10123
10124
10125VBOXDDU_DECL(int) VDAsyncRead(PVBOXHDD pDisk, uint64_t uOffset, size_t cbRead,
10126 PCRTSGBUF pcSgBuf,
10127 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
10128 void *pvUser1, void *pvUser2)
10129{
10130 int rc = VERR_VD_BLOCK_FREE;
10131 int rc2;
10132 bool fLockRead = false;
10133 PVDIOCTX pIoCtx = NULL;
10134
10135 LogFlowFunc(("pDisk=%#p uOffset=%llu pcSgBuf=%#p cbRead=%zu pvUser1=%#p pvUser2=%#p\n",
10136 pDisk, uOffset, pcSgBuf, cbRead, pvUser1, pvUser2));
10137
10138 do
10139 {
10140 /* sanity check */
10141 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10142 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10143
10144 /* Check arguments. */
10145 AssertMsgBreakStmt(cbRead,
10146 ("cbRead=%zu\n", cbRead),
10147 rc = VERR_INVALID_PARAMETER);
10148 AssertMsgBreakStmt(VALID_PTR(pcSgBuf),
10149 ("pcSgBuf=%#p\n", pcSgBuf),
10150 rc = VERR_INVALID_PARAMETER);
10151
10152 rc2 = vdThreadStartRead(pDisk);
10153 AssertRC(rc2);
10154 fLockRead = true;
10155
10156 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
10157 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
10158 uOffset, cbRead, pDisk->cbSize),
10159 rc = VERR_INVALID_PARAMETER);
10160 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
10161
10162 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_READ, uOffset,
10163 cbRead, pDisk->pLast, pcSgBuf,
10164 pfnComplete, pvUser1, pvUser2,
10165 NULL, vdReadHelperAsync,
10166 VDIOCTX_FLAGS_ZERO_FREE_BLOCKS);
10167 if (!pIoCtx)
10168 {
10169 rc = VERR_NO_MEMORY;
10170 break;
10171 }
10172
10173 rc = vdIoCtxProcessTryLockDefer(pIoCtx);
10174 if (rc == VINF_VD_ASYNC_IO_FINISHED)
10175 {
10176 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
10177 vdIoCtxFree(pDisk, pIoCtx);
10178 else
10179 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
10180 }
10181 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
10182 vdIoCtxFree(pDisk, pIoCtx);
10183
10184 } while (0);
10185
10186 if (RT_UNLIKELY(fLockRead) && ( rc == VINF_VD_ASYNC_IO_FINISHED
10187 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
10188 {
10189 rc2 = vdThreadFinishRead(pDisk);
10190 AssertRC(rc2);
10191 }
10192
10193 LogFlowFunc(("returns %Rrc\n", rc));
10194 return rc;
10195}
10196
10197
10198VBOXDDU_DECL(int) VDAsyncWrite(PVBOXHDD pDisk, uint64_t uOffset, size_t cbWrite,
10199 PCRTSGBUF pcSgBuf,
10200 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
10201 void *pvUser1, void *pvUser2)
10202{
10203 int rc;
10204 int rc2;
10205 bool fLockWrite = false;
10206 PVDIOCTX pIoCtx = NULL;
10207
10208 LogFlowFunc(("pDisk=%#p uOffset=%llu cSgBuf=%#p cbWrite=%zu pvUser1=%#p pvUser2=%#p\n",
10209 pDisk, uOffset, pcSgBuf, cbWrite, pvUser1, pvUser2));
10210 do
10211 {
10212 /* sanity check */
10213 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10214 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10215
10216 /* Check arguments. */
10217 AssertMsgBreakStmt(cbWrite,
10218 ("cbWrite=%zu\n", cbWrite),
10219 rc = VERR_INVALID_PARAMETER);
10220 AssertMsgBreakStmt(VALID_PTR(pcSgBuf),
10221 ("pcSgBuf=%#p\n", pcSgBuf),
10222 rc = VERR_INVALID_PARAMETER);
10223
10224 rc2 = vdThreadStartWrite(pDisk);
10225 AssertRC(rc2);
10226 fLockWrite = true;
10227
10228 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
10229 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
10230 uOffset, cbWrite, pDisk->cbSize),
10231 rc = VERR_INVALID_PARAMETER);
10232 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
10233
10234 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_WRITE, uOffset,
10235 cbWrite, pDisk->pLast, pcSgBuf,
10236 pfnComplete, pvUser1, pvUser2,
10237 NULL, vdWriteHelperAsync,
10238 VDIOCTX_FLAGS_DEFAULT);
10239 if (!pIoCtx)
10240 {
10241 rc = VERR_NO_MEMORY;
10242 break;
10243 }
10244
10245 rc = vdIoCtxProcessTryLockDefer(pIoCtx);
10246 if (rc == VINF_VD_ASYNC_IO_FINISHED)
10247 {
10248 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
10249 vdIoCtxFree(pDisk, pIoCtx);
10250 else
10251 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
10252 }
10253 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
10254 vdIoCtxFree(pDisk, pIoCtx);
10255 } while (0);
10256
10257 if (RT_UNLIKELY(fLockWrite) && ( rc == VINF_VD_ASYNC_IO_FINISHED
10258 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
10259 {
10260 rc2 = vdThreadFinishWrite(pDisk);
10261 AssertRC(rc2);
10262 }
10263
10264 LogFlowFunc(("returns %Rrc\n", rc));
10265 return rc;
10266}
10267
10268
10269VBOXDDU_DECL(int) VDAsyncFlush(PVBOXHDD pDisk, PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
10270 void *pvUser1, void *pvUser2)
10271{
10272 int rc;
10273 int rc2;
10274 bool fLockWrite = false;
10275 PVDIOCTX pIoCtx = NULL;
10276
10277 LogFlowFunc(("pDisk=%#p\n", pDisk));
10278
10279 do
10280 {
10281 /* sanity check */
10282 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10283 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10284
10285 rc2 = vdThreadStartWrite(pDisk);
10286 AssertRC(rc2);
10287 fLockWrite = true;
10288
10289 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
10290
10291 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_FLUSH, 0,
10292 0, pDisk->pLast, NULL,
10293 pfnComplete, pvUser1, pvUser2,
10294 NULL, vdFlushHelperAsync,
10295 VDIOCTX_FLAGS_DEFAULT);
10296 if (!pIoCtx)
10297 {
10298 rc = VERR_NO_MEMORY;
10299 break;
10300 }
10301
10302 rc = vdIoCtxProcessTryLockDefer(pIoCtx);
10303 if (rc == VINF_VD_ASYNC_IO_FINISHED)
10304 {
10305 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
10306 vdIoCtxFree(pDisk, pIoCtx);
10307 else
10308 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
10309 }
10310 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
10311 vdIoCtxFree(pDisk, pIoCtx);
10312 } while (0);
10313
10314 if (RT_UNLIKELY(fLockWrite) && ( rc == VINF_VD_ASYNC_IO_FINISHED
10315 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
10316 {
10317 rc2 = vdThreadFinishWrite(pDisk);
10318 AssertRC(rc2);
10319 }
10320
10321 LogFlowFunc(("returns %Rrc\n", rc));
10322 return rc;
10323}
10324
10325VBOXDDU_DECL(int) VDAsyncDiscardRanges(PVBOXHDD pDisk, PCRTRANGE paRanges, unsigned cRanges,
10326 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
10327 void *pvUser1, void *pvUser2)
10328{
10329 int rc;
10330 int rc2;
10331 bool fLockWrite = false;
10332 PVDIOCTX pIoCtx = NULL;
10333
10334 LogFlowFunc(("pDisk=%#p\n", pDisk));
10335
10336 do
10337 {
10338 /* sanity check */
10339 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
10340 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
10341
10342 rc2 = vdThreadStartWrite(pDisk);
10343 AssertRC(rc2);
10344 fLockWrite = true;
10345
10346 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
10347
10348 pIoCtx = vdIoCtxDiscardAlloc(pDisk, paRanges, cRanges,
10349 pfnComplete, pvUser1, pvUser2, NULL,
10350 vdDiscardHelperAsync,
10351 VDIOCTX_FLAGS_DEFAULT);
10352 if (!pIoCtx)
10353 {
10354 rc = VERR_NO_MEMORY;
10355 break;
10356 }
10357
10358 rc = vdIoCtxProcessTryLockDefer(pIoCtx);
10359 if (rc == VINF_VD_ASYNC_IO_FINISHED)
10360 {
10361 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
10362 vdIoCtxFree(pDisk, pIoCtx);
10363 else
10364 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
10365 }
10366 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
10367 vdIoCtxFree(pDisk, pIoCtx);
10368 } while (0);
10369
10370 if (RT_UNLIKELY(fLockWrite) && ( rc == VINF_VD_ASYNC_IO_FINISHED
10371 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
10372 {
10373 rc2 = vdThreadFinishWrite(pDisk);
10374 AssertRC(rc2);
10375 }
10376
10377 LogFlowFunc(("returns %Rrc\n", rc));
10378 return rc;
10379}
10380
10381VBOXDDU_DECL(int) VDRepair(PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
10382 const char *pszFilename, const char *pszBackend,
10383 uint32_t fFlags)
10384{
10385 int rc = VERR_NOT_SUPPORTED;
10386 PCVBOXHDDBACKEND pBackend = NULL;
10387 VDINTERFACEIOINT VDIfIoInt;
10388 VDINTERFACEIO VDIfIoFallback;
10389 PVDINTERFACEIO pInterfaceIo;
10390
10391 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
10392 /* Check arguments. */
10393 AssertMsgReturn(VALID_PTR(pszFilename) && *pszFilename,
10394 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
10395 VERR_INVALID_PARAMETER);
10396 AssertMsgReturn(VALID_PTR(pszBackend),
10397 ("pszBackend=%#p\n", pszBackend),
10398 VERR_INVALID_PARAMETER);
10399 AssertMsgReturn((fFlags & ~VD_REPAIR_FLAGS_MASK) == 0,
10400 ("fFlags=%#x\n", fFlags),
10401 VERR_INVALID_PARAMETER);
10402
10403 pInterfaceIo = VDIfIoGet(pVDIfsImage);
10404 if (!pInterfaceIo)
10405 {
10406 /*
10407 * Caller doesn't provide an I/O interface, create our own using the
10408 * native file API.
10409 */
10410 vdIfIoFallbackCallbacksSetup(&VDIfIoFallback);
10411 pInterfaceIo = &VDIfIoFallback;
10412 }
10413
10414 /* Set up the internal I/O interface. */
10415 AssertReturn(!VDIfIoIntGet(pVDIfsImage), VERR_INVALID_PARAMETER);
10416 VDIfIoInt.pfnOpen = vdIOIntOpenLimited;
10417 VDIfIoInt.pfnClose = vdIOIntCloseLimited;
10418 VDIfIoInt.pfnDelete = vdIOIntDeleteLimited;
10419 VDIfIoInt.pfnMove = vdIOIntMoveLimited;
10420 VDIfIoInt.pfnGetFreeSpace = vdIOIntGetFreeSpaceLimited;
10421 VDIfIoInt.pfnGetModificationTime = vdIOIntGetModificationTimeLimited;
10422 VDIfIoInt.pfnGetSize = vdIOIntGetSizeLimited;
10423 VDIfIoInt.pfnSetSize = vdIOIntSetSizeLimited;
10424 VDIfIoInt.pfnReadUser = vdIOIntReadUserLimited;
10425 VDIfIoInt.pfnWriteUser = vdIOIntWriteUserLimited;
10426 VDIfIoInt.pfnReadMeta = vdIOIntReadMetaLimited;
10427 VDIfIoInt.pfnWriteMeta = vdIOIntWriteMetaLimited;
10428 VDIfIoInt.pfnFlush = vdIOIntFlushLimited;
10429 rc = VDInterfaceAdd(&VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
10430 pInterfaceIo, sizeof(VDINTERFACEIOINT), &pVDIfsImage);
10431 AssertRC(rc);
10432
10433 rc = vdFindBackend(pszBackend, &pBackend);
10434 if (RT_SUCCESS(rc))
10435 {
10436 if (pBackend->pfnRepair)
10437 rc = pBackend->pfnRepair(pszFilename, pVDIfsDisk, pVDIfsImage, fFlags);
10438 else
10439 rc = VERR_VD_IMAGE_REPAIR_NOT_SUPPORTED;
10440 }
10441
10442 LogFlowFunc(("returns %Rrc\n", rc));
10443 return rc;
10444}
10445
Note: See TracBrowser for help on using the repository browser.

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