VirtualBox

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

Last change on this file since 50653 was 49944, checked in by vboxsync, 10 years ago

Storage,VHD: Fix I/O if VD_OPEN_FLAGS_HONOR_SAME is used

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

© 2023 Oracle
ContactPrivacy policyTerms of Use