VirtualBox

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

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

Storage: Propagate errors when closing a file but free everything nevertheless (see @bugref{6791})

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