VirtualBox

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

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

Storage: Preparations for the sync/async I/O unification

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