VirtualBox

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

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

Storage: Update parent image override when creating a child I/O context, fixes merging images

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