VirtualBox

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

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

Storage: Fix possible data corruption or read errors if a request accesses the same region while another write is active and allocating a new data block in the image

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

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