VirtualBox

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

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

pr6938. Error VERR_DISK_FULL isn't replaced by VINF_VD_ASYNC_IO_FINISHED anymore when is returned from vdIoCtxProcessLocked().

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

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