VirtualBox

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

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

Storage/VD: Fix more places to properly handle the ignore flush flag during opening and creating images

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