VirtualBox

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

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

Storage: Small fix to previous change

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