VirtualBox

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

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

VD.cpp: Don't assert when reading beyond the end of a floppy image. We frequently (boot sector tests for instance) attach images that are smaller than the disk size guessed by the guest. Just return blocks filled with good old F6h for the missing parts of floppies.

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