VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/VBoxHDD.cpp@ 33223

Last change on this file since 33223 was 33094, checked in by vboxsync, 14 years ago

VD: Fix an assertion when queuing a flush while an expanding write request is still active. Rename variables.

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