VirtualBox

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

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

Storage: Fix hang for sync I/O under some circumstances

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

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