VirtualBox

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

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

Storage/FilterCrypt: Remove hardcoded data unit size of 512 bytes and introduce interface to query the size from the generic layer

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