VirtualBox

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

最後變更 在這個檔案從38409是 38409,由 vboxsync 提交於 14 年 前

VD: Fix a possible deadlock if the block cache is enabled

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

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