VirtualBox

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

Last change on this file since 41255 was 41244, checked in by vboxsync, 13 years ago

Storage/VD: tweak error handling a bit so that file access errors are not seen as success (which confuses callers)

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

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