VirtualBox

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

最後變更 在這個檔案從44794是 44794,由 vboxsync 提交於 12 年 前

Storage: Another try to unify sync and async I/O code

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

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