VirtualBox

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

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

VD: API to repair corrupted images

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