VirtualBox

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

Last change on this file since 46757 was 46712, checked in by vboxsync, 12 years ago

Storage: Don't set the modified flag when copying images with VDCopy, fixes non sequential access assertions when exporting to OVA

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