VirtualBox

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

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

VD: Add option to ignore flush requests

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