VirtualBox

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

Last change on this file since 44616 was 44616, checked in by vboxsync, 12 years ago

Storage/VD: Remove the leftovers of the sync I/O path, everything is covered by the async I/O path now

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