VirtualBox

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

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

Storage/VD: Remove disk full hack and use the proper way to return the status code which is the rcReq field of te I/O context.

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

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