VirtualBox

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

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

Storage: Fix assertion when using the iSCSI backend

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