VirtualBox

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

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

Storage: Make it possible to set VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS during runtime

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

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