VirtualBox

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

最後變更 在這個檔案從49880是 49880,由 vboxsync 提交於 11 年 前

Storage/VD: Fix bug which causes that data is read twice. Because there is no room left in the buffer an error is returned

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

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