VirtualBox

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

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

VD: New VDCopyEx() API which can speedup cloning of diff images when cloning a VM

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 303.8 KB
Line 
1/* $Id: VD.cpp 38203 2011-07-27 16:13: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.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_VD
22#include <VBox/vd.h>
23#include <VBox/err.h>
24#include <VBox/sup.h>
25#include <VBox/log.h>
26
27#include <iprt/alloc.h>
28#include <iprt/assert.h>
29#include <iprt/uuid.h>
30#include <iprt/file.h>
31#include <iprt/string.h>
32#include <iprt/asm.h>
33#include <iprt/ldr.h>
34#include <iprt/dir.h>
35#include <iprt/path.h>
36#include <iprt/param.h>
37#include <iprt/memcache.h>
38#include <iprt/sg.h>
39#include <iprt/critsect.h>
40#include <iprt/list.h>
41#include <iprt/avl.h>
42
43#include <VBox/vd-plugin.h>
44#include <VBox/vd-cache-plugin.h>
45
46#define VBOXHDDDISK_SIGNATURE 0x6f0e2a7d
47
48/** Buffer size used for merging images. */
49#define VD_MERGE_BUFFER_SIZE (16 * _1M)
50
51/** Maximum number of segments in one I/O task. */
52#define VD_IO_TASK_SEGMENTS_MAX 64
53
54/**
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 if (RT_FAILURE(rcReq))
2766 ASMAtomicCmpXchgS32(&pIoCtx->rcReq, rcReq, VINF_SUCCESS);
2767
2768 if (!pIoCtx->fBlocked)
2769 {
2770 /* Continue the transfer */
2771 rc = vdIoCtxProcess(pIoCtx);
2772
2773 if ( rc == VINF_VD_ASYNC_IO_FINISHED
2774 && ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
2775 {
2776 LogFlowFunc(("I/O context completed pIoCtx=%#p\n", pIoCtx));
2777 if (pIoCtx->pIoCtxParent)
2778 {
2779 PVDIOCTX pIoCtxParent = pIoCtx->pIoCtxParent;
2780
2781 Assert(!pIoCtxParent->pIoCtxParent);
2782 if (RT_FAILURE(pIoCtx->rcReq))
2783 ASMAtomicCmpXchgS32(&pIoCtxParent->rcReq, pIoCtx->rcReq, VINF_SUCCESS);
2784
2785 if (pIoCtx->enmTxDir == VDIOCTXTXDIR_WRITE)
2786 {
2787 LogFlowFunc(("I/O context transferred %u bytes for the parent pIoCtxParent=%p\n",
2788 pIoCtx->Type.Child.cbTransferParent, pIoCtxParent));
2789
2790 /* Update the parent state. */
2791 Assert(pIoCtxParent->cbTransferLeft >= pIoCtx->Type.Child.cbTransferParent);
2792 ASMAtomicSubU32(&pIoCtxParent->cbTransferLeft, pIoCtx->Type.Child.cbTransferParent);
2793 }
2794 else
2795 Assert(pIoCtx->enmTxDir == VDIOCTXTXDIR_FLUSH);
2796
2797 /*
2798 * A completed child write means that we finished growing the image.
2799 * We have to process any pending writes now.
2800 */
2801 vdIoCtxUnlockDisk(pDisk, pIoCtxParent, false /* fProcessDeferredReqs */);
2802
2803 /* Unblock the parent */
2804 pIoCtxParent->fBlocked = false;
2805
2806 rc = vdIoCtxProcess(pIoCtxParent);
2807
2808 if ( rc == VINF_VD_ASYNC_IO_FINISHED
2809 && ASMAtomicCmpXchgBool(&pIoCtxParent->fComplete, true, false))
2810 {
2811 RTCritSectLeave(&pDisk->CritSect);
2812 LogFlowFunc(("Parent I/O context completed pIoCtxParent=%#p rcReq=%Rrc\n", pIoCtxParent, pIoCtxParent->rcReq));
2813 pIoCtxParent->Type.Root.pfnComplete(pIoCtxParent->Type.Root.pvUser1,
2814 pIoCtxParent->Type.Root.pvUser2,
2815 pIoCtxParent->rcReq);
2816 vdThreadFinishWrite(pDisk);
2817 vdIoCtxFree(pDisk, pIoCtxParent);
2818 RTCritSectEnter(&pDisk->CritSect);
2819 }
2820
2821 /* Process any pending writes if the current request didn't caused another growing. */
2822 RTCritSectEnter(&pDisk->CritSect);
2823
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 RTCritSectLeave(&pDisk->CritSect);
2860 LogFlowFunc(("Waiting I/O context completed pIoCtxWait=%#p\n", pIoCtxWait));
2861 vdThreadFinishWrite(pDisk);
2862 pIoCtxWait->Type.Root.pfnComplete(pIoCtxWait->Type.Root.pvUser1,
2863 pIoCtxWait->Type.Root.pvUser2,
2864 pIoCtxWait->rcReq);
2865 vdIoCtxFree(pDisk, pIoCtxWait);
2866 RTCritSectEnter(&pDisk->CritSect);
2867 }
2868 } while (!RTListIsEmpty(&ListTmp));
2869 }
2870 else
2871 RTCritSectLeave(&pDisk->CritSect);
2872 }
2873 else
2874 {
2875 if (pIoCtx->enmTxDir == VDIOCTXTXDIR_FLUSH)
2876 {
2877 vdIoCtxUnlockDisk(pDisk, pIoCtx, true /* fProcessDerredReqs */);
2878 vdThreadFinishWrite(pDisk);
2879 }
2880 else if (pIoCtx->enmTxDir == VDIOCTXTXDIR_WRITE)
2881 vdThreadFinishWrite(pDisk);
2882 else
2883 {
2884 Assert(pIoCtx->enmTxDir == VDIOCTXTXDIR_READ);
2885 vdThreadFinishRead(pDisk);
2886 }
2887
2888 LogFlowFunc(("I/O context completed pIoCtx=%#p rcReq=%Rrc\n", pIoCtx, pIoCtx->rcReq));
2889 RTCritSectLeave(&pDisk->CritSect);
2890 pIoCtx->Type.Root.pfnComplete(pIoCtx->Type.Root.pvUser1,
2891 pIoCtx->Type.Root.pvUser2,
2892 pIoCtx->rcReq);
2893 RTCritSectEnter(&pDisk->CritSect);
2894 }
2895
2896 vdIoCtxFree(pDisk, pIoCtx);
2897 }
2898 }
2899
2900 return VINF_SUCCESS;
2901}
2902
2903/**
2904 * Internal - Called when user transfer completed.
2905 */
2906static int vdUserXferCompleted(PVDIOSTORAGE pIoStorage, PVDIOCTX pIoCtx,
2907 PFNVDXFERCOMPLETED pfnComplete, void *pvUser,
2908 size_t cbTransfer, int rcReq)
2909{
2910 int rc = VINF_SUCCESS;
2911 bool fIoCtxContinue = true;
2912 PVBOXHDD pDisk = pIoCtx->pDisk;
2913
2914 LogFlowFunc(("pIoStorage=%#p pIoCtx=%#p pfnComplete=%#p pvUser=%#p cbTransfer=%zu rcReq=%Rrc\n",
2915 pIoStorage, pIoCtx, pfnComplete, pvUser, cbTransfer, rcReq));
2916
2917 RTCritSectEnter(&pDisk->CritSect);
2918 Assert(pIoCtx->cbTransferLeft >= cbTransfer);
2919 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbTransfer);
2920 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
2921
2922 if (pfnComplete)
2923 rc = pfnComplete(pIoStorage->pVDIo->pBackendData, pIoCtx, pvUser, rcReq);
2924
2925 if (RT_SUCCESS(rc))
2926 rc = vdIoCtxContinue(pIoCtx, rcReq);
2927 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2928 rc = VINF_SUCCESS;
2929
2930 RTCritSectLeave(&pDisk->CritSect);
2931
2932 return rc;
2933}
2934
2935/**
2936 * Internal - Called when a meta transfer completed.
2937 */
2938static int vdMetaXferCompleted(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnComplete, void *pvUser,
2939 PVDMETAXFER pMetaXfer, int rcReq)
2940{
2941 PVBOXHDD pDisk = pIoStorage->pVDIo->pDisk;
2942 RTLISTNODE ListIoCtxWaiting;
2943 bool fFlush;
2944
2945 LogFlowFunc(("pIoStorage=%#p pfnComplete=%#p pvUser=%#p pMetaXfer=%#p rcReq=%Rrc\n",
2946 pIoStorage, pfnComplete, pvUser, pMetaXfer, rcReq));
2947
2948 RTCritSectEnter(&pDisk->CritSect);
2949 fFlush = VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_FLUSH;
2950 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
2951
2952 if (!fFlush)
2953 {
2954 RTListMove(&ListIoCtxWaiting, &pMetaXfer->ListIoCtxWaiting);
2955
2956 if (RT_FAILURE(rcReq))
2957 {
2958 /* Remove from the AVL tree. */
2959 LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
2960 bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
2961 Assert(fRemoved);
2962 RTMemFree(pMetaXfer);
2963 }
2964 else
2965 {
2966 /* Increase the reference counter to make sure it doesn't go away before the last context is processed. */
2967 pMetaXfer->cRefs++;
2968 }
2969 }
2970 else
2971 RTListMove(&ListIoCtxWaiting, &pMetaXfer->ListIoCtxWaiting);
2972
2973 /* Go through the waiting list and continue the I/O contexts. */
2974 while (!RTListIsEmpty(&ListIoCtxWaiting))
2975 {
2976 int rc = VINF_SUCCESS;
2977 bool fContinue = true;
2978 PVDIOCTXDEFERRED pDeferred = RTListGetFirst(&ListIoCtxWaiting, VDIOCTXDEFERRED, NodeDeferred);
2979 PVDIOCTX pIoCtx = pDeferred->pIoCtx;
2980 RTListNodeRemove(&pDeferred->NodeDeferred);
2981
2982 RTMemFree(pDeferred);
2983 ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
2984
2985 if (pfnComplete)
2986 rc = pfnComplete(pIoStorage->pVDIo->pBackendData, pIoCtx, pvUser, rcReq);
2987
2988 LogFlow(("Completion callback for I/O context %#p returned %Rrc\n", pIoCtx, rc));
2989
2990 if (RT_SUCCESS(rc))
2991 {
2992 rc = vdIoCtxContinue(pIoCtx, rcReq);
2993 AssertRC(rc);
2994 }
2995 else
2996 Assert(rc == VERR_VD_ASYNC_IO_IN_PROGRESS);
2997 }
2998
2999 /* Remove if not used anymore. */
3000 if (RT_SUCCESS(rcReq) && !fFlush)
3001 {
3002 pMetaXfer->cRefs--;
3003 if (!pMetaXfer->cRefs && RTListIsEmpty(&pMetaXfer->ListIoCtxWaiting))
3004 {
3005 /* Remove from the AVL tree. */
3006 LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
3007 bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
3008 Assert(fRemoved);
3009 RTMemFree(pMetaXfer);
3010 }
3011 }
3012 else if (fFlush)
3013 RTMemFree(pMetaXfer);
3014
3015 RTCritSectLeave(&pDisk->CritSect);
3016
3017 return VINF_SUCCESS;
3018}
3019
3020static int vdIOIntReqCompleted(void *pvUser, int rcReq)
3021{
3022 int rc = VINF_SUCCESS;
3023 PVDIOTASK pIoTask = (PVDIOTASK)pvUser;
3024 PVDIOSTORAGE pIoStorage = pIoTask->pIoStorage;
3025
3026 LogFlowFunc(("Task completed pIoTask=%#p\n", pIoTask));
3027
3028 if (!pIoTask->fMeta)
3029 rc = vdUserXferCompleted(pIoStorage, pIoTask->Type.User.pIoCtx,
3030 pIoTask->pfnComplete, pIoTask->pvUser,
3031 pIoTask->Type.User.cbTransfer, rcReq);
3032 else
3033 rc = vdMetaXferCompleted(pIoStorage, pIoTask->pfnComplete, pIoTask->pvUser,
3034 pIoTask->Type.Meta.pMetaXfer, rcReq);
3035
3036 vdIoTaskFree(pIoStorage->pVDIo->pDisk, pIoTask);
3037
3038 return rc;
3039}
3040
3041/**
3042 * VD I/O interface callback for opening a file.
3043 */
3044static int vdIOIntOpen(void *pvUser, const char *pszLocation,
3045 unsigned uOpenFlags, PPVDIOSTORAGE ppIoStorage)
3046{
3047 int rc = VINF_SUCCESS;
3048 PVDIO pVDIo = (PVDIO)pvUser;
3049 PVDIOSTORAGE pIoStorage = (PVDIOSTORAGE)RTMemAllocZ(sizeof(VDIOSTORAGE));
3050
3051 if (!pIoStorage)
3052 return VERR_NO_MEMORY;
3053
3054 /* Create the AVl tree. */
3055 pIoStorage->pTreeMetaXfers = (PAVLRFOFFTREE)RTMemAllocZ(sizeof(AVLRFOFFTREE));
3056 if (pIoStorage->pTreeMetaXfers)
3057 {
3058 rc = pVDIo->pInterfaceIOCallbacks->pfnOpen(pVDIo->pInterfaceIO->pvUser,
3059 pszLocation, uOpenFlags,
3060 vdIOIntReqCompleted,
3061 &pIoStorage->pStorage);
3062 if (RT_SUCCESS(rc))
3063 {
3064 pIoStorage->pVDIo = pVDIo;
3065 *ppIoStorage = pIoStorage;
3066 return VINF_SUCCESS;
3067 }
3068
3069 RTMemFree(pIoStorage->pTreeMetaXfers);
3070 }
3071 else
3072 rc = VERR_NO_MEMORY;
3073
3074 RTMemFree(pIoStorage);
3075 return rc;
3076}
3077
3078static int vdIOIntTreeMetaXferDestroy(PAVLRFOFFNODECORE pNode, void *pvUser)
3079{
3080 AssertMsgFailed(("Tree should be empty at this point!\n"));
3081 return VINF_SUCCESS;
3082}
3083
3084static int vdIOIntClose(void *pvUser, PVDIOSTORAGE pIoStorage)
3085{
3086 PVDIO pVDIo = (PVDIO)pvUser;
3087
3088 int rc = pVDIo->pInterfaceIOCallbacks->pfnClose(pVDIo->pInterfaceIO->pvUser,
3089 pIoStorage->pStorage);
3090 AssertRC(rc);
3091
3092 RTAvlrFileOffsetDestroy(pIoStorage->pTreeMetaXfers, vdIOIntTreeMetaXferDestroy, NULL);
3093 RTMemFree(pIoStorage->pTreeMetaXfers);
3094 RTMemFree(pIoStorage);
3095 return VINF_SUCCESS;
3096}
3097
3098static int vdIOIntDelete(void *pvUser, const char *pcszFilename)
3099{
3100 PVDIO pVDIo = (PVDIO)pvUser;
3101 return pVDIo->pInterfaceIOCallbacks->pfnDelete(pVDIo->pInterfaceIO->pvUser,
3102 pcszFilename);
3103}
3104
3105static int vdIOIntMove(void *pvUser, const char *pcszSrc, const char *pcszDst,
3106 unsigned fMove)
3107{
3108 PVDIO pVDIo = (PVDIO)pvUser;
3109 return pVDIo->pInterfaceIOCallbacks->pfnMove(pVDIo->pInterfaceIO->pvUser,
3110 pcszSrc, pcszDst, fMove);
3111}
3112
3113static int vdIOIntGetFreeSpace(void *pvUser, const char *pcszFilename,
3114 int64_t *pcbFreeSpace)
3115{
3116 PVDIO pVDIo = (PVDIO)pvUser;
3117 return pVDIo->pInterfaceIOCallbacks->pfnGetFreeSpace(pVDIo->pInterfaceIO->pvUser,
3118 pcszFilename,
3119 pcbFreeSpace);
3120}
3121
3122static int vdIOIntGetModificationTime(void *pvUser, const char *pcszFilename,
3123 PRTTIMESPEC pModificationTime)
3124{
3125 PVDIO pVDIo = (PVDIO)pvUser;
3126 return pVDIo->pInterfaceIOCallbacks->pfnGetModificationTime(pVDIo->pInterfaceIO->pvUser,
3127 pcszFilename,
3128 pModificationTime);
3129}
3130
3131static int vdIOIntGetSize(void *pvUser, PVDIOSTORAGE pIoStorage,
3132 uint64_t *pcbSize)
3133{
3134 PVDIO pVDIo = (PVDIO)pvUser;
3135 return pVDIo->pInterfaceIOCallbacks->pfnGetSize(pVDIo->pInterfaceIO->pvUser,
3136 pIoStorage->pStorage,
3137 pcbSize);
3138}
3139
3140static int vdIOIntSetSize(void *pvUser, PVDIOSTORAGE pIoStorage,
3141 uint64_t cbSize)
3142{
3143 PVDIO pVDIo = (PVDIO)pvUser;
3144
3145 return pVDIo->pInterfaceIOCallbacks->pfnSetSize(pVDIo->pInterfaceIO->pvUser,
3146 pIoStorage->pStorage,
3147 cbSize);
3148}
3149
3150static int vdIOIntWriteSync(void *pvUser, PVDIOSTORAGE pIoStorage,
3151 uint64_t uOffset, const void *pvBuf,
3152 size_t cbWrite, size_t *pcbWritten)
3153{
3154 PVDIO pVDIo = (PVDIO)pvUser;
3155
3156 return pVDIo->pInterfaceIOCallbacks->pfnWriteSync(pVDIo->pInterfaceIO->pvUser,
3157 pIoStorage->pStorage,
3158 uOffset, pvBuf, cbWrite,
3159 pcbWritten);
3160}
3161
3162static int vdIOIntReadSync(void *pvUser, PVDIOSTORAGE pIoStorage,
3163 uint64_t uOffset, void *pvBuf, size_t cbRead,
3164 size_t *pcbRead)
3165{
3166 PVDIO pVDIo = (PVDIO)pvUser;
3167 return pVDIo->pInterfaceIOCallbacks->pfnReadSync(pVDIo->pInterfaceIO->pvUser,
3168 pIoStorage->pStorage,
3169 uOffset, pvBuf, cbRead,
3170 pcbRead);
3171}
3172
3173static int vdIOIntFlushSync(void *pvUser, PVDIOSTORAGE pIoStorage)
3174{
3175 PVDIO pVDIo = (PVDIO)pvUser;
3176 return pVDIo->pInterfaceIOCallbacks->pfnFlushSync(pVDIo->pInterfaceIO->pvUser,
3177 pIoStorage->pStorage);
3178}
3179
3180static int vdIOIntReadUserAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
3181 uint64_t uOffset, PVDIOCTX pIoCtx,
3182 size_t cbRead)
3183{
3184 int rc = VINF_SUCCESS;
3185 PVDIO pVDIo = (PVDIO)pvUser;
3186 PVBOXHDD pDisk = pVDIo->pDisk;
3187
3188 LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pIoCtx=%#p cbRead=%u\n",
3189 pvUser, pIoStorage, uOffset, pIoCtx, cbRead));
3190
3191 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3192
3193 Assert(cbRead > 0);
3194
3195 /* Build the S/G array and spawn a new I/O task */
3196 while (cbRead)
3197 {
3198 RTSGSEG aSeg[VD_IO_TASK_SEGMENTS_MAX];
3199 unsigned cSegments = VD_IO_TASK_SEGMENTS_MAX;
3200 size_t cbTaskRead = 0;
3201
3202 cbTaskRead = RTSgBufSegArrayCreate(&pIoCtx->SgBuf, aSeg, &cSegments, cbRead);
3203
3204 Assert(cSegments > 0);
3205 Assert(cbTaskRead > 0);
3206 AssertMsg(cbTaskRead <= cbRead, ("Invalid number of bytes to read\n"));
3207
3208 LogFlow(("Reading %u bytes into %u segments\n", cbTaskRead, cSegments));
3209
3210#ifdef RT_STRICT
3211 for (unsigned i = 0; i < cSegments; i++)
3212 AssertMsg(aSeg[i].pvSeg && !(aSeg[i].cbSeg % 512),
3213 ("Segment %u is invalid\n", i));
3214#endif
3215
3216 PVDIOTASK pIoTask = vdIoTaskUserAlloc(pIoStorage, NULL, NULL, pIoCtx, cbTaskRead);
3217
3218 if (!pIoTask)
3219 return VERR_NO_MEMORY;
3220
3221 ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
3222
3223 void *pvTask;
3224 rc = pVDIo->pInterfaceIOCallbacks->pfnReadAsync(pVDIo->pInterfaceIO->pvUser,
3225 pIoStorage->pStorage,
3226 uOffset, aSeg, cSegments,
3227 cbTaskRead, pIoTask,
3228 &pvTask);
3229 if (RT_SUCCESS(rc))
3230 {
3231 AssertMsg(cbTaskRead <= pIoCtx->cbTransferLeft, ("Impossible!\n"));
3232 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbTaskRead);
3233 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
3234 vdIoTaskFree(pDisk, pIoTask);
3235 }
3236 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
3237 {
3238 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
3239 vdIoTaskFree(pDisk, pIoTask);
3240 break;
3241 }
3242
3243 uOffset += cbTaskRead;
3244 cbRead -= cbTaskRead;
3245 }
3246
3247 LogFlowFunc(("returns rc=%Rrc\n", rc));
3248 return rc;
3249}
3250
3251static int vdIOIntWriteUserAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
3252 uint64_t uOffset, PVDIOCTX pIoCtx,
3253 size_t cbWrite,
3254 PFNVDXFERCOMPLETED pfnComplete,
3255 void *pvCompleteUser)
3256{
3257 int rc = VINF_SUCCESS;
3258 PVDIO pVDIo = (PVDIO)pvUser;
3259 PVBOXHDD pDisk = pVDIo->pDisk;
3260
3261 LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pIoCtx=%#p cbWrite=%u\n",
3262 pvUser, pIoStorage, uOffset, pIoCtx, cbWrite));
3263
3264 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3265
3266 Assert(cbWrite > 0);
3267
3268 /* Build the S/G array and spawn a new I/O task */
3269 while (cbWrite)
3270 {
3271 RTSGSEG aSeg[VD_IO_TASK_SEGMENTS_MAX];
3272 unsigned cSegments = VD_IO_TASK_SEGMENTS_MAX;
3273 size_t cbTaskWrite = 0;
3274
3275 cbTaskWrite = RTSgBufSegArrayCreate(&pIoCtx->SgBuf, aSeg, &cSegments, cbWrite);
3276
3277 Assert(cSegments > 0);
3278 Assert(cbTaskWrite > 0);
3279 AssertMsg(cbTaskWrite <= cbWrite, ("Invalid number of bytes to write\n"));
3280
3281 LogFlow(("Writing %u bytes from %u segments\n", cbTaskWrite, cSegments));
3282
3283#ifdef DEBUG
3284 for (unsigned i = 0; i < cSegments; i++)
3285 AssertMsg(aSeg[i].pvSeg && !(aSeg[i].cbSeg % 512),
3286 ("Segment %u is invalid\n", i));
3287#endif
3288
3289 PVDIOTASK pIoTask = vdIoTaskUserAlloc(pIoStorage, pfnComplete, pvCompleteUser, pIoCtx, cbTaskWrite);
3290
3291 if (!pIoTask)
3292 return VERR_NO_MEMORY;
3293
3294 ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
3295
3296 void *pvTask;
3297 rc = pVDIo->pInterfaceIOCallbacks->pfnWriteAsync(pVDIo->pInterfaceIO->pvUser,
3298 pIoStorage->pStorage,
3299 uOffset, aSeg, cSegments,
3300 cbTaskWrite, pIoTask,
3301 &pvTask);
3302 if (RT_SUCCESS(rc))
3303 {
3304 AssertMsg(cbTaskWrite <= pIoCtx->cbTransferLeft, ("Impossible!\n"));
3305 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbTaskWrite);
3306 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
3307 vdIoTaskFree(pDisk, pIoTask);
3308 }
3309 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
3310 {
3311 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
3312 vdIoTaskFree(pDisk, pIoTask);
3313 break;
3314 }
3315
3316 uOffset += cbTaskWrite;
3317 cbWrite -= cbTaskWrite;
3318 }
3319
3320 return rc;
3321}
3322
3323static int vdIOIntReadMetaAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
3324 uint64_t uOffset, void *pvBuf,
3325 size_t cbRead, PVDIOCTX pIoCtx,
3326 PPVDMETAXFER ppMetaXfer,
3327 PFNVDXFERCOMPLETED pfnComplete,
3328 void *pvCompleteUser)
3329{
3330 PVDIO pVDIo = (PVDIO)pvUser;
3331 PVBOXHDD pDisk = pVDIo->pDisk;
3332 int rc = VINF_SUCCESS;
3333 RTSGSEG Seg;
3334 PVDIOTASK pIoTask;
3335 PVDMETAXFER pMetaXfer = NULL;
3336 void *pvTask = NULL;
3337
3338 LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pvBuf=%#p cbRead=%u\n",
3339 pvUser, pIoStorage, uOffset, pvBuf, cbRead));
3340
3341 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3342
3343 pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGet(pIoStorage->pTreeMetaXfers, uOffset);
3344 if (!pMetaXfer)
3345 {
3346#ifdef RT_STRICT
3347 pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGetBestFit(pIoStorage->pTreeMetaXfers, uOffset, false /* fAbove */);
3348 AssertMsg(!pMetaXfer || (pMetaXfer->Core.Key + (RTFOFF)pMetaXfer->cbMeta <= (RTFOFF)uOffset),
3349 ("Overlapping meta transfers!\n"));
3350#endif
3351
3352 /* Allocate a new meta transfer. */
3353 pMetaXfer = vdMetaXferAlloc(pIoStorage, uOffset, cbRead);
3354 if (!pMetaXfer)
3355 return VERR_NO_MEMORY;
3356
3357 pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvCompleteUser, pMetaXfer);
3358 if (!pIoTask)
3359 {
3360 RTMemFree(pMetaXfer);
3361 return VERR_NO_MEMORY;
3362 }
3363
3364 Seg.cbSeg = cbRead;
3365 Seg.pvSeg = pMetaXfer->abData;
3366
3367 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_READ);
3368 rc = pVDIo->pInterfaceIOCallbacks->pfnReadAsync(pVDIo->pInterfaceIO->pvUser,
3369 pIoStorage->pStorage,
3370 uOffset, &Seg, 1,
3371 cbRead, pIoTask,
3372 &pvTask);
3373
3374 if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
3375 {
3376 bool fInserted = RTAvlrFileOffsetInsert(pIoStorage->pTreeMetaXfers, &pMetaXfer->Core);
3377 Assert(fInserted);
3378 }
3379 else
3380 RTMemFree(pMetaXfer);
3381
3382 if (RT_SUCCESS(rc))
3383 {
3384 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
3385 vdIoTaskFree(pDisk, pIoTask);
3386 }
3387 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS && !pfnComplete)
3388 rc = VERR_VD_NOT_ENOUGH_METADATA;
3389 }
3390
3391 Assert(VALID_PTR(pMetaXfer) || RT_FAILURE(rc));
3392
3393 if (RT_SUCCESS(rc) || rc == VERR_VD_NOT_ENOUGH_METADATA || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
3394 {
3395 /* If it is pending add the request to the list. */
3396 if (VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_READ)
3397 {
3398 PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
3399 AssertPtr(pDeferred);
3400
3401 RTListInit(&pDeferred->NodeDeferred);
3402 pDeferred->pIoCtx = pIoCtx;
3403
3404 ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
3405 RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
3406 rc = VERR_VD_NOT_ENOUGH_METADATA;
3407 }
3408 else
3409 {
3410 /* Transfer the data. */
3411 pMetaXfer->cRefs++;
3412 Assert(pMetaXfer->cbMeta >= cbRead);
3413 Assert(pMetaXfer->Core.Key == (RTFOFF)uOffset);
3414 memcpy(pvBuf, pMetaXfer->abData, cbRead);
3415 *ppMetaXfer = pMetaXfer;
3416 }
3417 }
3418
3419 return rc;
3420}
3421
3422static int vdIOIntWriteMetaAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
3423 uint64_t uOffset, void *pvBuf,
3424 size_t cbWrite, PVDIOCTX pIoCtx,
3425 PFNVDXFERCOMPLETED pfnComplete,
3426 void *pvCompleteUser)
3427{
3428 PVDIO pVDIo = (PVDIO)pvUser;
3429 PVBOXHDD pDisk = pVDIo->pDisk;
3430 int rc = VINF_SUCCESS;
3431 RTSGSEG Seg;
3432 PVDIOTASK pIoTask;
3433 PVDMETAXFER pMetaXfer = NULL;
3434 bool fInTree = false;
3435 void *pvTask = NULL;
3436
3437 LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pvBuf=%#p cbWrite=%u\n",
3438 pvUser, pIoStorage, uOffset, pvBuf, cbWrite));
3439
3440 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3441
3442 pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGet(pIoStorage->pTreeMetaXfers, uOffset);
3443 if (!pMetaXfer)
3444 {
3445 /* Allocate a new meta transfer. */
3446 pMetaXfer = vdMetaXferAlloc(pIoStorage, uOffset, cbWrite);
3447 if (!pMetaXfer)
3448 return VERR_NO_MEMORY;
3449 }
3450 else
3451 {
3452 Assert(pMetaXfer->cbMeta >= cbWrite);
3453 Assert(pMetaXfer->Core.Key == (RTFOFF)uOffset);
3454 fInTree = true;
3455 }
3456
3457 Assert(VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE);
3458
3459 pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvCompleteUser, pMetaXfer);
3460 if (!pIoTask)
3461 {
3462 RTMemFree(pMetaXfer);
3463 return VERR_NO_MEMORY;
3464 }
3465
3466 memcpy(pMetaXfer->abData, pvBuf, cbWrite);
3467 Seg.cbSeg = cbWrite;
3468 Seg.pvSeg = pMetaXfer->abData;
3469
3470 ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
3471
3472 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_WRITE);
3473 rc = pVDIo->pInterfaceIOCallbacks->pfnWriteAsync(pVDIo->pInterfaceIO->pvUser,
3474 pIoStorage->pStorage,
3475 uOffset, &Seg, 1,
3476 cbWrite, pIoTask,
3477 &pvTask);
3478 if (RT_SUCCESS(rc))
3479 {
3480 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
3481 ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
3482 vdIoTaskFree(pDisk, pIoTask);
3483 if (fInTree && !pMetaXfer->cRefs)
3484 {
3485 LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
3486 bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
3487 AssertMsg(fRemoved, ("Metadata transfer wasn't removed\n"));
3488 RTMemFree(pMetaXfer);
3489 pMetaXfer = NULL;
3490 }
3491 }
3492 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
3493 {
3494 PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
3495 AssertPtr(pDeferred);
3496
3497 RTListInit(&pDeferred->NodeDeferred);
3498 pDeferred->pIoCtx = pIoCtx;
3499
3500 if (!fInTree)
3501 {
3502 bool fInserted = RTAvlrFileOffsetInsert(pIoStorage->pTreeMetaXfers, &pMetaXfer->Core);
3503 Assert(fInserted);
3504 }
3505
3506 RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
3507 }
3508 else
3509 {
3510 RTMemFree(pMetaXfer);
3511 pMetaXfer = NULL;
3512 }
3513
3514 return rc;
3515}
3516
3517static void vdIOIntMetaXferRelease(void *pvUser, PVDMETAXFER pMetaXfer)
3518{
3519 PVDIO pVDIo = (PVDIO)pvUser;
3520 PVBOXHDD pDisk = pVDIo->pDisk;
3521 PVDIOSTORAGE pIoStorage = pMetaXfer->pIoStorage;
3522
3523 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3524
3525 Assert( VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE
3526 || VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_WRITE);
3527 Assert(pMetaXfer->cRefs > 0);
3528
3529 pMetaXfer->cRefs--;
3530 if ( !pMetaXfer->cRefs
3531 && RTListIsEmpty(&pMetaXfer->ListIoCtxWaiting)
3532 && VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE)
3533 {
3534 /* Free the meta data entry. */
3535 LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
3536 bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
3537 AssertMsg(fRemoved, ("Metadata transfer wasn't removed\n"));
3538
3539 RTMemFree(pMetaXfer);
3540 }
3541}
3542
3543static int vdIOIntFlushAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
3544 PVDIOCTX pIoCtx, PFNVDXFERCOMPLETED pfnComplete,
3545 void *pvCompleteUser)
3546{
3547 PVDIO pVDIo = (PVDIO)pvUser;
3548 PVBOXHDD pDisk = pVDIo->pDisk;
3549 int rc = VINF_SUCCESS;
3550 PVDIOTASK pIoTask;
3551 PVDMETAXFER pMetaXfer = NULL;
3552 void *pvTask = NULL;
3553
3554 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3555
3556 LogFlowFunc(("pvUser=%#p pIoStorage=%#p pIoCtx=%#p\n",
3557 pvUser, pIoStorage, pIoCtx));
3558
3559 /* Allocate a new meta transfer. */
3560 pMetaXfer = vdMetaXferAlloc(pIoStorage, 0, 0);
3561 if (!pMetaXfer)
3562 return VERR_NO_MEMORY;
3563
3564 pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvUser, pMetaXfer);
3565 if (!pIoTask)
3566 {
3567 RTMemFree(pMetaXfer);
3568 return VERR_NO_MEMORY;
3569 }
3570
3571 ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
3572
3573 PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
3574 AssertPtr(pDeferred);
3575
3576 RTListInit(&pDeferred->NodeDeferred);
3577 pDeferred->pIoCtx = pIoCtx;
3578
3579 RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
3580 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_FLUSH);
3581 rc = pVDIo->pInterfaceIOCallbacks->pfnFlushAsync(pVDIo->pInterfaceIO->pvUser,
3582 pIoStorage->pStorage,
3583 pIoTask, &pvTask);
3584 if (RT_SUCCESS(rc))
3585 {
3586 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
3587 ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
3588 vdIoTaskFree(pDisk, pIoTask);
3589 RTMemFree(pDeferred);
3590 RTMemFree(pMetaXfer);
3591 }
3592 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
3593 RTMemFree(pMetaXfer);
3594
3595 return rc;
3596}
3597
3598static size_t vdIOIntIoCtxCopyTo(void *pvUser, PVDIOCTX pIoCtx,
3599 void *pvBuf, size_t cbBuf)
3600{
3601 PVDIO pVDIo = (PVDIO)pvUser;
3602 PVBOXHDD pDisk = pVDIo->pDisk;
3603 size_t cbCopied = 0;
3604
3605 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3606
3607 cbCopied = vdIoCtxCopyTo(pIoCtx, (uint8_t *)pvBuf, cbBuf);
3608 Assert(cbCopied == cbBuf);
3609
3610 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbCopied);
3611
3612 return cbCopied;
3613}
3614
3615static size_t vdIOIntIoCtxCopyFrom(void *pvUser, PVDIOCTX pIoCtx,
3616 void *pvBuf, size_t cbBuf)
3617{
3618 PVDIO pVDIo = (PVDIO)pvUser;
3619 PVBOXHDD pDisk = pVDIo->pDisk;
3620 size_t cbCopied = 0;
3621
3622 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3623
3624 cbCopied = vdIoCtxCopyFrom(pIoCtx, (uint8_t *)pvBuf, cbBuf);
3625 Assert(cbCopied == cbBuf);
3626
3627 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbCopied);
3628
3629 return cbCopied;
3630}
3631
3632static size_t vdIOIntIoCtxSet(void *pvUser, PVDIOCTX pIoCtx, int ch, size_t cb)
3633{
3634 PVDIO pVDIo = (PVDIO)pvUser;
3635 PVBOXHDD pDisk = pVDIo->pDisk;
3636 size_t cbSet = 0;
3637
3638 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3639
3640 cbSet = vdIoCtxSet(pIoCtx, ch, cb);
3641 Assert(cbSet == cb);
3642
3643 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbSet);
3644
3645 return cbSet;
3646}
3647
3648static size_t vdIOIntIoCtxSegArrayCreate(void *pvUser, PVDIOCTX pIoCtx,
3649 PRTSGSEG paSeg, unsigned *pcSeg,
3650 size_t cbData)
3651{
3652 PVDIO pVDIo = (PVDIO)pvUser;
3653 PVBOXHDD pDisk = pVDIo->pDisk;
3654 size_t cbCreated = 0;
3655
3656 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3657
3658 cbCreated = RTSgBufSegArrayCreate(&pIoCtx->SgBuf, paSeg, pcSeg, cbData);
3659 Assert(!paSeg || cbData == cbCreated);
3660
3661 return cbCreated;
3662}
3663
3664static void vdIOIntIoCtxCompleted(void *pvUser, PVDIOCTX pIoCtx, int rcReq,
3665 size_t cbCompleted)
3666{
3667 PVDIO pVDIo = (PVDIO)pvUser;
3668 PVBOXHDD pDisk = pVDIo->pDisk;
3669
3670 /*
3671 * Grab the disk critical section to avoid races with other threads which
3672 * might still modify the I/O context.
3673 * Example is that iSCSI is doing an asynchronous write but calls us already
3674 * while the other thread is still hanging in vdWriteHelperAsync and couldn't update
3675 * the fBlocked state yet.
3676 * It can overwrite the state to true before we call vdIoCtxContinue and the
3677 * the request would hang indefinite.
3678 */
3679 int rc = RTCritSectEnter(&pDisk->CritSect);
3680 AssertRC(rc);
3681
3682 /* Continue */
3683 pIoCtx->fBlocked = false;
3684 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbCompleted);
3685
3686 /* Clear the pointer to next transfer function in case we have nothing to transfer anymore.
3687 * @todo: Find a better way to prevent vdIoCtxContinue from calling the read/write helper again. */
3688 if (!pIoCtx->cbTransferLeft)
3689 pIoCtx->pfnIoCtxTransfer = NULL;
3690
3691 vdIoCtxContinue(pIoCtx, rcReq);
3692
3693 rc = RTCritSectLeave(&pDisk->CritSect);
3694 AssertRC(rc);
3695}
3696
3697/**
3698 * VD I/O interface callback for opening a file (limited version for VDGetFormat).
3699 */
3700static int vdIOIntOpenLimited(void *pvUser, const char *pszLocation,
3701 uint32_t fOpen, PPVDIOSTORAGE ppIoStorage)
3702{
3703 int rc = VINF_SUCCESS;
3704 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3705 PVDIOSTORAGE pIoStorage = (PVDIOSTORAGE)RTMemAllocZ(sizeof(VDIOSTORAGE));
3706
3707 if (!pIoStorage)
3708 return VERR_NO_MEMORY;
3709
3710 rc = pInterfaceIOCallbacks->pfnOpen(NULL, pszLocation, fOpen,
3711 NULL, &pIoStorage->pStorage);
3712 if (RT_SUCCESS(rc))
3713 *ppIoStorage = pIoStorage;
3714 else
3715 RTMemFree(pIoStorage);
3716
3717 return rc;
3718}
3719
3720static int vdIOIntCloseLimited(void *pvUser, PVDIOSTORAGE pIoStorage)
3721{
3722 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3723 int rc = pInterfaceIOCallbacks->pfnClose(NULL, pIoStorage->pStorage);
3724 AssertRC(rc);
3725
3726 RTMemFree(pIoStorage);
3727 return VINF_SUCCESS;
3728}
3729
3730static int vdIOIntDeleteLimited(void *pvUser, const char *pcszFilename)
3731{
3732 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3733 return pInterfaceIOCallbacks->pfnDelete(NULL, pcszFilename);
3734}
3735
3736static int vdIOIntMoveLimited(void *pvUser, const char *pcszSrc,
3737 const char *pcszDst, unsigned fMove)
3738{
3739 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3740 return pInterfaceIOCallbacks->pfnMove(NULL, pcszSrc, pcszDst, fMove);
3741}
3742
3743static int vdIOIntGetFreeSpaceLimited(void *pvUser, const char *pcszFilename,
3744 int64_t *pcbFreeSpace)
3745{
3746 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3747 return pInterfaceIOCallbacks->pfnGetFreeSpace(NULL, pcszFilename, pcbFreeSpace);
3748}
3749
3750static int vdIOIntGetModificationTimeLimited(void *pvUser,
3751 const char *pcszFilename,
3752 PRTTIMESPEC pModificationTime)
3753{
3754 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3755 return pInterfaceIOCallbacks->pfnGetModificationTime(NULL, pcszFilename, pModificationTime);
3756}
3757
3758static int vdIOIntGetSizeLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
3759 uint64_t *pcbSize)
3760{
3761 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3762 return pInterfaceIOCallbacks->pfnGetSize(NULL, pIoStorage->pStorage, pcbSize);
3763}
3764
3765static int vdIOIntSetSizeLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
3766 uint64_t cbSize)
3767{
3768 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3769 return pInterfaceIOCallbacks->pfnSetSize(NULL, pIoStorage->pStorage, cbSize);
3770}
3771
3772static int vdIOIntWriteSyncLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
3773 uint64_t uOffset, const void *pvBuf,
3774 size_t cbWrite, size_t *pcbWritten)
3775{
3776 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3777 return pInterfaceIOCallbacks->pfnWriteSync(NULL, pIoStorage->pStorage, uOffset, pvBuf, cbWrite, pcbWritten);
3778}
3779
3780static int vdIOIntReadSyncLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
3781 uint64_t uOffset, void *pvBuf, size_t cbRead,
3782 size_t *pcbRead)
3783{
3784 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3785 return pInterfaceIOCallbacks->pfnReadSync(NULL, pIoStorage->pStorage, uOffset, pvBuf, cbRead, pcbRead);
3786}
3787
3788static int vdIOIntFlushSyncLimited(void *pvUser, PVDIOSTORAGE pIoStorage)
3789{
3790 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3791 return pInterfaceIOCallbacks->pfnFlushSync(NULL, pIoStorage->pStorage);
3792}
3793
3794/**
3795 * internal: send output to the log (unconditionally).
3796 */
3797int vdLogMessage(void *pvUser, const char *pszFormat, va_list args)
3798{
3799 NOREF(pvUser);
3800 RTLogPrintfV(pszFormat, args);
3801 return VINF_SUCCESS;
3802}
3803
3804DECLINLINE(int) vdMessageWrapper(PVBOXHDD pDisk, const char *pszFormat, ...)
3805{
3806 va_list va;
3807 va_start(va, pszFormat);
3808 int rc = pDisk->pInterfaceErrorCallbacks->pfnMessage(pDisk->pInterfaceError->pvUser,
3809 pszFormat, va);
3810 va_end(va);
3811 return rc;
3812}
3813
3814
3815/**
3816 * internal: adjust PCHS geometry
3817 */
3818static void vdFixupPCHSGeometry(PVDGEOMETRY pPCHS, uint64_t cbSize)
3819{
3820 /* Fix broken PCHS geometry. Can happen for two reasons: either the backend
3821 * mixes up PCHS and LCHS, or the application used to create the source
3822 * image has put garbage in it. Additionally, if the PCHS geometry covers
3823 * more than the image size, set it back to the default. */
3824 if ( pPCHS->cHeads > 16
3825 || pPCHS->cSectors > 63
3826 || pPCHS->cCylinders == 0
3827 || (uint64_t)pPCHS->cHeads * pPCHS->cSectors * pPCHS->cCylinders * 512 > cbSize)
3828 {
3829 Assert(!(RT_MIN(cbSize / 512 / 16 / 63, 16383) - (uint32_t)RT_MIN(cbSize / 512 / 16 / 63, 16383)));
3830 pPCHS->cCylinders = (uint32_t)RT_MIN(cbSize / 512 / 16 / 63, 16383);
3831 pPCHS->cHeads = 16;
3832 pPCHS->cSectors = 63;
3833 }
3834}
3835
3836/**
3837 * internal: adjust PCHS geometry
3838 */
3839static void vdFixupLCHSGeometry(PVDGEOMETRY pLCHS, uint64_t cbSize)
3840{
3841 /* Fix broken LCHS geometry. Can happen for two reasons: either the backend
3842 * mixes up PCHS and LCHS, or the application used to create the source
3843 * image has put garbage in it. The fix in this case is to clear the LCHS
3844 * geometry to trigger autodetection when it is used next. If the geometry
3845 * already says "please autodetect" (cylinders=0) keep it. */
3846 if ( ( pLCHS->cHeads > 255
3847 || pLCHS->cHeads == 0
3848 || pLCHS->cSectors > 63
3849 || pLCHS->cSectors == 0)
3850 && pLCHS->cCylinders != 0)
3851 {
3852 pLCHS->cCylinders = 0;
3853 pLCHS->cHeads = 0;
3854 pLCHS->cSectors = 0;
3855 }
3856 /* Always recompute the number of cylinders stored in the LCHS
3857 * geometry if it isn't set to "autotedetect" at the moment.
3858 * This is very useful if the destination image size is
3859 * larger or smaller than the source image size. Do not modify
3860 * the number of heads and sectors. Windows guests hate it. */
3861 if ( pLCHS->cCylinders != 0
3862 && pLCHS->cHeads != 0 /* paranoia */
3863 && pLCHS->cSectors != 0 /* paranoia */)
3864 {
3865 Assert(!(RT_MIN(cbSize / 512 / pLCHS->cHeads / pLCHS->cSectors, 1024) - (uint32_t)RT_MIN(cbSize / 512 / pLCHS->cHeads / pLCHS->cSectors, 1024)));
3866 pLCHS->cCylinders = (uint32_t)RT_MIN(cbSize / 512 / pLCHS->cHeads / pLCHS->cSectors, 1024);
3867 }
3868}
3869
3870/**
3871 * Initializes HDD backends.
3872 *
3873 * @returns VBox status code.
3874 */
3875VBOXDDU_DECL(int) VDInit(void)
3876{
3877 int rc = vdAddBackends(aStaticBackends, RT_ELEMENTS(aStaticBackends));
3878 if (RT_SUCCESS(rc))
3879 {
3880 rc = vdAddCacheBackends(aStaticCacheBackends, RT_ELEMENTS(aStaticCacheBackends));
3881 if (RT_SUCCESS(rc))
3882 {
3883 rc = vdLoadDynamicBackends();
3884 if (RT_SUCCESS(rc))
3885 rc = vdLoadDynamicCacheBackends();
3886 }
3887 }
3888 LogRel(("VDInit finished\n"));
3889 return rc;
3890}
3891
3892/**
3893 * Destroys loaded HDD backends.
3894 *
3895 * @returns VBox status code.
3896 */
3897VBOXDDU_DECL(int) VDShutdown(void)
3898{
3899 PVBOXHDDBACKEND *pBackends = g_apBackends;
3900 PVDCACHEBACKEND *pCacheBackends = g_apCacheBackends;
3901 unsigned cBackends = g_cBackends;
3902
3903 if (!pBackends)
3904 return VERR_INTERNAL_ERROR;
3905
3906 g_cBackends = 0;
3907 g_apBackends = NULL;
3908
3909#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
3910 for (unsigned i = 0; i < cBackends; i++)
3911 if (pBackends[i]->hPlugin != NIL_RTLDRMOD)
3912 RTLdrClose(pBackends[i]->hPlugin);
3913#endif
3914
3915 /* Clear the supported cache backends. */
3916 cBackends = g_cCacheBackends;
3917 g_cCacheBackends = 0;
3918 g_apCacheBackends = NULL;
3919
3920#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
3921 for (unsigned i = 0; i < cBackends; i++)
3922 if (pCacheBackends[i]->hPlugin != NIL_RTLDRMOD)
3923 RTLdrClose(pCacheBackends[i]->hPlugin);
3924#endif
3925
3926 if (pCacheBackends)
3927 RTMemFree(pCacheBackends);
3928 RTMemFree(pBackends);
3929 return VINF_SUCCESS;
3930}
3931
3932
3933/**
3934 * Lists all HDD backends and their capabilities in a caller-provided buffer.
3935 *
3936 * @returns VBox status code.
3937 * VERR_BUFFER_OVERFLOW if not enough space is passed.
3938 * @param cEntriesAlloc Number of list entries available.
3939 * @param pEntries Pointer to array for the entries.
3940 * @param pcEntriesUsed Number of entries returned.
3941 */
3942VBOXDDU_DECL(int) VDBackendInfo(unsigned cEntriesAlloc, PVDBACKENDINFO pEntries,
3943 unsigned *pcEntriesUsed)
3944{
3945 int rc = VINF_SUCCESS;
3946 PRTDIR pPluginDir = NULL;
3947 unsigned cEntries = 0;
3948
3949 LogFlowFunc(("cEntriesAlloc=%u pEntries=%#p pcEntriesUsed=%#p\n", cEntriesAlloc, pEntries, pcEntriesUsed));
3950 /* Check arguments. */
3951 AssertMsgReturn(cEntriesAlloc,
3952 ("cEntriesAlloc=%u\n", cEntriesAlloc),
3953 VERR_INVALID_PARAMETER);
3954 AssertMsgReturn(VALID_PTR(pEntries),
3955 ("pEntries=%#p\n", pEntries),
3956 VERR_INVALID_PARAMETER);
3957 AssertMsgReturn(VALID_PTR(pcEntriesUsed),
3958 ("pcEntriesUsed=%#p\n", pcEntriesUsed),
3959 VERR_INVALID_PARAMETER);
3960 if (!g_apBackends)
3961 VDInit();
3962
3963 if (cEntriesAlloc < g_cBackends)
3964 {
3965 *pcEntriesUsed = g_cBackends;
3966 return VERR_BUFFER_OVERFLOW;
3967 }
3968
3969 for (unsigned i = 0; i < g_cBackends; i++)
3970 {
3971 pEntries[i].pszBackend = g_apBackends[i]->pszBackendName;
3972 pEntries[i].uBackendCaps = g_apBackends[i]->uBackendCaps;
3973 pEntries[i].paFileExtensions = g_apBackends[i]->paFileExtensions;
3974 pEntries[i].paConfigInfo = g_apBackends[i]->paConfigInfo;
3975 pEntries[i].pfnComposeLocation = g_apBackends[i]->pfnComposeLocation;
3976 pEntries[i].pfnComposeName = g_apBackends[i]->pfnComposeName;
3977 }
3978
3979 LogFlowFunc(("returns %Rrc *pcEntriesUsed=%u\n", rc, cEntries));
3980 *pcEntriesUsed = g_cBackends;
3981 return rc;
3982}
3983
3984/**
3985 * Lists the capabilities of a backend identified by its name.
3986 *
3987 * @returns VBox status code.
3988 * @param pszBackend The backend name.
3989 * @param pEntries Pointer to an entry.
3990 */
3991VBOXDDU_DECL(int) VDBackendInfoOne(const char *pszBackend, PVDBACKENDINFO pEntry)
3992{
3993 LogFlowFunc(("pszBackend=%#p pEntry=%#p\n", pszBackend, pEntry));
3994 /* Check arguments. */
3995 AssertMsgReturn(VALID_PTR(pszBackend),
3996 ("pszBackend=%#p\n", pszBackend),
3997 VERR_INVALID_PARAMETER);
3998 AssertMsgReturn(VALID_PTR(pEntry),
3999 ("pEntry=%#p\n", pEntry),
4000 VERR_INVALID_PARAMETER);
4001 if (!g_apBackends)
4002 VDInit();
4003
4004 /* Go through loaded backends. */
4005 for (unsigned i = 0; i < g_cBackends; i++)
4006 {
4007 if (!RTStrICmp(pszBackend, g_apBackends[i]->pszBackendName))
4008 {
4009 pEntry->pszBackend = g_apBackends[i]->pszBackendName;
4010 pEntry->uBackendCaps = g_apBackends[i]->uBackendCaps;
4011 pEntry->paFileExtensions = g_apBackends[i]->paFileExtensions;
4012 pEntry->paConfigInfo = g_apBackends[i]->paConfigInfo;
4013 return VINF_SUCCESS;
4014 }
4015 }
4016
4017 return VERR_NOT_FOUND;
4018}
4019
4020/**
4021 * Allocates and initializes an empty HDD container.
4022 * No image files are opened.
4023 *
4024 * @returns VBox status code.
4025 * @param pVDIfsDisk Pointer to the per-disk VD interface list.
4026 * @param enmType Type of the image container.
4027 * @param ppDisk Where to store the reference to HDD container.
4028 */
4029VBOXDDU_DECL(int) VDCreate(PVDINTERFACE pVDIfsDisk, VDTYPE enmType, PVBOXHDD *ppDisk)
4030{
4031 int rc = VINF_SUCCESS;
4032 PVBOXHDD pDisk = NULL;
4033
4034 LogFlowFunc(("pVDIfsDisk=%#p\n", pVDIfsDisk));
4035 do
4036 {
4037 /* Check arguments. */
4038 AssertMsgBreakStmt(VALID_PTR(ppDisk),
4039 ("ppDisk=%#p\n", ppDisk),
4040 rc = VERR_INVALID_PARAMETER);
4041
4042 pDisk = (PVBOXHDD)RTMemAllocZ(sizeof(VBOXHDD));
4043 if (pDisk)
4044 {
4045 pDisk->u32Signature = VBOXHDDDISK_SIGNATURE;
4046 pDisk->enmType = enmType;
4047 pDisk->cImages = 0;
4048 pDisk->pBase = NULL;
4049 pDisk->pLast = NULL;
4050 pDisk->cbSize = 0;
4051 pDisk->PCHSGeometry.cCylinders = 0;
4052 pDisk->PCHSGeometry.cHeads = 0;
4053 pDisk->PCHSGeometry.cSectors = 0;
4054 pDisk->LCHSGeometry.cCylinders = 0;
4055 pDisk->LCHSGeometry.cHeads = 0;
4056 pDisk->LCHSGeometry.cSectors = 0;
4057 pDisk->pVDIfsDisk = pVDIfsDisk;
4058 pDisk->pInterfaceError = NULL;
4059 pDisk->pInterfaceErrorCallbacks = NULL;
4060 pDisk->pInterfaceThreadSync = NULL;
4061 pDisk->pInterfaceThreadSyncCallbacks = NULL;
4062 pDisk->fLocked = false;
4063 pDisk->pIoCtxLockOwner = NULL;
4064 RTListInit(&pDisk->ListWriteLocked);
4065
4066 /* Create the I/O ctx cache */
4067 rc = RTMemCacheCreate(&pDisk->hMemCacheIoCtx, sizeof(VDIOCTX), 0, UINT32_MAX,
4068 NULL, NULL, NULL, 0);
4069 if (RT_FAILURE(rc))
4070 {
4071 RTMemFree(pDisk);
4072 break;
4073 }
4074
4075 /* Create the I/O task cache */
4076 rc = RTMemCacheCreate(&pDisk->hMemCacheIoTask, sizeof(VDIOTASK), 0, UINT32_MAX,
4077 NULL, NULL, NULL, 0);
4078 if (RT_FAILURE(rc))
4079 {
4080 RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
4081 RTMemFree(pDisk);
4082 break;
4083 }
4084
4085 /* Create critical section. */
4086 rc = RTCritSectInit(&pDisk->CritSect);
4087 if (RT_FAILURE(rc))
4088 {
4089 RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
4090 RTMemCacheDestroy(pDisk->hMemCacheIoTask);
4091 RTMemFree(pDisk);
4092 break;
4093 }
4094
4095 pDisk->pInterfaceError = VDInterfaceGet(pVDIfsDisk, VDINTERFACETYPE_ERROR);
4096 if (pDisk->pInterfaceError)
4097 pDisk->pInterfaceErrorCallbacks = VDGetInterfaceError(pDisk->pInterfaceError);
4098
4099 pDisk->pInterfaceThreadSync = VDInterfaceGet(pVDIfsDisk, VDINTERFACETYPE_THREADSYNC);
4100 if (pDisk->pInterfaceThreadSync)
4101 pDisk->pInterfaceThreadSyncCallbacks = VDGetInterfaceThreadSync(pDisk->pInterfaceThreadSync);
4102
4103 /* Create fallback I/O callback table */
4104 pDisk->VDIIOCallbacks.cbSize = sizeof(VDINTERFACEIO);
4105 pDisk->VDIIOCallbacks.enmInterface = VDINTERFACETYPE_IO;
4106 pDisk->VDIIOCallbacks.pfnOpen = vdIOOpenFallback;
4107 pDisk->VDIIOCallbacks.pfnClose = vdIOCloseFallback;
4108 pDisk->VDIIOCallbacks.pfnDelete = vdIODeleteFallback;
4109 pDisk->VDIIOCallbacks.pfnMove = vdIOMoveFallback;
4110 pDisk->VDIIOCallbacks.pfnGetFreeSpace = vdIOGetFreeSpaceFallback;
4111 pDisk->VDIIOCallbacks.pfnGetModificationTime = vdIOGetModificationTimeFallback;
4112 pDisk->VDIIOCallbacks.pfnGetSize = vdIOGetSizeFallback;
4113 pDisk->VDIIOCallbacks.pfnSetSize = vdIOSetSizeFallback;
4114 pDisk->VDIIOCallbacks.pfnReadSync = vdIOReadSyncFallback;
4115 pDisk->VDIIOCallbacks.pfnWriteSync = vdIOWriteSyncFallback;
4116 pDisk->VDIIOCallbacks.pfnFlushSync = vdIOFlushSyncFallback;
4117 pDisk->VDIIOCallbacks.pfnReadAsync = vdIOReadAsyncFallback;
4118 pDisk->VDIIOCallbacks.pfnWriteAsync = vdIOWriteAsyncFallback;
4119 pDisk->VDIIOCallbacks.pfnFlushAsync = vdIOFlushAsyncFallback;
4120
4121 /*
4122 * Create the internal I/O callback table.
4123 * The interface is per-image but no need to duplicate the
4124 * callback table every time.
4125 */
4126 pDisk->VDIIOIntCallbacks.cbSize = sizeof(VDINTERFACEIOINT);
4127 pDisk->VDIIOIntCallbacks.enmInterface = VDINTERFACETYPE_IOINT;
4128 pDisk->VDIIOIntCallbacks.pfnOpen = vdIOIntOpen;
4129 pDisk->VDIIOIntCallbacks.pfnClose = vdIOIntClose;
4130 pDisk->VDIIOIntCallbacks.pfnDelete = vdIOIntDelete;
4131 pDisk->VDIIOIntCallbacks.pfnMove = vdIOIntMove;
4132 pDisk->VDIIOIntCallbacks.pfnGetFreeSpace = vdIOIntGetFreeSpace;
4133 pDisk->VDIIOIntCallbacks.pfnGetModificationTime = vdIOIntGetModificationTime;
4134 pDisk->VDIIOIntCallbacks.pfnGetSize = vdIOIntGetSize;
4135 pDisk->VDIIOIntCallbacks.pfnSetSize = vdIOIntSetSize;
4136 pDisk->VDIIOIntCallbacks.pfnReadSync = vdIOIntReadSync;
4137 pDisk->VDIIOIntCallbacks.pfnWriteSync = vdIOIntWriteSync;
4138 pDisk->VDIIOIntCallbacks.pfnFlushSync = vdIOIntFlushSync;
4139 pDisk->VDIIOIntCallbacks.pfnReadUserAsync = vdIOIntReadUserAsync;
4140 pDisk->VDIIOIntCallbacks.pfnWriteUserAsync = vdIOIntWriteUserAsync;
4141 pDisk->VDIIOIntCallbacks.pfnReadMetaAsync = vdIOIntReadMetaAsync;
4142 pDisk->VDIIOIntCallbacks.pfnWriteMetaAsync = vdIOIntWriteMetaAsync;
4143 pDisk->VDIIOIntCallbacks.pfnMetaXferRelease = vdIOIntMetaXferRelease;
4144 pDisk->VDIIOIntCallbacks.pfnFlushAsync = vdIOIntFlushAsync;
4145 pDisk->VDIIOIntCallbacks.pfnIoCtxCopyFrom = vdIOIntIoCtxCopyFrom;
4146 pDisk->VDIIOIntCallbacks.pfnIoCtxCopyTo = vdIOIntIoCtxCopyTo;
4147 pDisk->VDIIOIntCallbacks.pfnIoCtxSet = vdIOIntIoCtxSet;
4148 pDisk->VDIIOIntCallbacks.pfnIoCtxSegArrayCreate = vdIOIntIoCtxSegArrayCreate;
4149 pDisk->VDIIOIntCallbacks.pfnIoCtxCompleted = vdIOIntIoCtxCompleted;
4150
4151 *ppDisk = pDisk;
4152 }
4153 else
4154 {
4155 rc = VERR_NO_MEMORY;
4156 break;
4157 }
4158 } while (0);
4159
4160 LogFlowFunc(("returns %Rrc (pDisk=%#p)\n", rc, pDisk));
4161 return rc;
4162}
4163
4164/**
4165 * Destroys HDD container.
4166 * If container has opened image files they will be closed.
4167 *
4168 * @param pDisk Pointer to HDD container.
4169 */
4170VBOXDDU_DECL(void) VDDestroy(PVBOXHDD pDisk)
4171{
4172 LogFlowFunc(("pDisk=%#p\n", pDisk));
4173 do
4174 {
4175 /* sanity check */
4176 AssertPtrBreak(pDisk);
4177 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4178 VDCloseAll(pDisk);
4179 RTCritSectDelete(&pDisk->CritSect);
4180 RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
4181 RTMemCacheDestroy(pDisk->hMemCacheIoTask);
4182 RTMemFree(pDisk);
4183 } while (0);
4184 LogFlowFunc(("returns\n"));
4185}
4186
4187/**
4188 * Try to get the backend name which can use this image.
4189 *
4190 * @returns VBox status code.
4191 * VINF_SUCCESS if a plugin was found.
4192 * ppszFormat contains the string which can be used as backend name.
4193 * VERR_NOT_SUPPORTED if no backend was found.
4194 * @param pVDIfsDisk Pointer to the per-disk VD interface list.
4195 * @param pVDIfsImage Pointer to the per-image VD interface list.
4196 * @param pszFilename Name of the image file for which the backend is queried.
4197 * @param ppszFormat Receives pointer of the UTF-8 string which contains the format name.
4198 * The returned pointer must be freed using RTStrFree().
4199 */
4200VBOXDDU_DECL(int) VDGetFormat(PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
4201 const char *pszFilename, char **ppszFormat, VDTYPE *penmType)
4202{
4203 int rc = VERR_NOT_SUPPORTED;
4204 VDINTERFACEIOINT VDIIOIntCallbacks;
4205 VDINTERFACE VDIIOInt;
4206 VDINTERFACEIO VDIIOCallbacksFallback;
4207 PVDINTERFACE pInterfaceIO;
4208 PVDINTERFACEIO pInterfaceIOCallbacks;
4209
4210 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
4211 /* Check arguments. */
4212 AssertMsgReturn(VALID_PTR(pszFilename) && *pszFilename,
4213 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
4214 VERR_INVALID_PARAMETER);
4215 AssertMsgReturn(VALID_PTR(ppszFormat),
4216 ("ppszFormat=%#p\n", ppszFormat),
4217 VERR_INVALID_PARAMETER);
4218 AssertMsgReturn(VALID_PTR(ppszFormat),
4219 ("penmType=%#p\n", penmType),
4220 VERR_INVALID_PARAMETER);
4221
4222 if (!g_apBackends)
4223 VDInit();
4224
4225 pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
4226 if (!pInterfaceIO)
4227 {
4228 /*
4229 * Caller doesn't provide an I/O interface, create our own using the
4230 * native file API.
4231 */
4232 VDIIOCallbacksFallback.cbSize = sizeof(VDINTERFACEIO);
4233 VDIIOCallbacksFallback.enmInterface = VDINTERFACETYPE_IO;
4234 VDIIOCallbacksFallback.pfnOpen = vdIOOpenFallback;
4235 VDIIOCallbacksFallback.pfnClose = vdIOCloseFallback;
4236 VDIIOCallbacksFallback.pfnDelete = vdIODeleteFallback;
4237 VDIIOCallbacksFallback.pfnMove = vdIOMoveFallback;
4238 VDIIOCallbacksFallback.pfnGetFreeSpace = vdIOGetFreeSpaceFallback;
4239 VDIIOCallbacksFallback.pfnGetModificationTime = vdIOGetModificationTimeFallback;
4240 VDIIOCallbacksFallback.pfnGetSize = vdIOGetSizeFallback;
4241 VDIIOCallbacksFallback.pfnSetSize = vdIOSetSizeFallback;
4242 VDIIOCallbacksFallback.pfnReadSync = vdIOReadSyncFallback;
4243 VDIIOCallbacksFallback.pfnWriteSync = vdIOWriteSyncFallback;
4244 VDIIOCallbacksFallback.pfnFlushSync = vdIOFlushSyncFallback;
4245 pInterfaceIOCallbacks = &VDIIOCallbacksFallback;
4246 }
4247 else
4248 pInterfaceIOCallbacks = VDGetInterfaceIO(pInterfaceIO);
4249
4250 /* Set up the internal I/O interface. */
4251 AssertReturn(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
4252 VERR_INVALID_PARAMETER);
4253 VDIIOIntCallbacks.cbSize = sizeof(VDINTERFACEIOINT);
4254 VDIIOIntCallbacks.enmInterface = VDINTERFACETYPE_IOINT;
4255 VDIIOIntCallbacks.pfnOpen = vdIOIntOpenLimited;
4256 VDIIOIntCallbacks.pfnClose = vdIOIntCloseLimited;
4257 VDIIOIntCallbacks.pfnDelete = vdIOIntDeleteLimited;
4258 VDIIOIntCallbacks.pfnMove = vdIOIntMoveLimited;
4259 VDIIOIntCallbacks.pfnGetFreeSpace = vdIOIntGetFreeSpaceLimited;
4260 VDIIOIntCallbacks.pfnGetModificationTime = vdIOIntGetModificationTimeLimited;
4261 VDIIOIntCallbacks.pfnGetSize = vdIOIntGetSizeLimited;
4262 VDIIOIntCallbacks.pfnSetSize = vdIOIntSetSizeLimited;
4263 VDIIOIntCallbacks.pfnReadSync = vdIOIntReadSyncLimited;
4264 VDIIOIntCallbacks.pfnWriteSync = vdIOIntWriteSyncLimited;
4265 VDIIOIntCallbacks.pfnFlushSync = vdIOIntFlushSyncLimited;
4266 VDIIOIntCallbacks.pfnReadUserAsync = NULL;
4267 VDIIOIntCallbacks.pfnWriteUserAsync = NULL;
4268 VDIIOIntCallbacks.pfnReadMetaAsync = NULL;
4269 VDIIOIntCallbacks.pfnWriteMetaAsync = NULL;
4270 VDIIOIntCallbacks.pfnFlushAsync = NULL;
4271 rc = VDInterfaceAdd(&VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
4272 &VDIIOIntCallbacks, pInterfaceIOCallbacks, &pVDIfsImage);
4273 AssertRC(rc);
4274
4275 /* Find the backend supporting this file format. */
4276 for (unsigned i = 0; i < g_cBackends; i++)
4277 {
4278 if (g_apBackends[i]->pfnCheckIfValid)
4279 {
4280 rc = g_apBackends[i]->pfnCheckIfValid(pszFilename, pVDIfsDisk,
4281 pVDIfsImage, penmType);
4282 if ( RT_SUCCESS(rc)
4283 /* The correct backend has been found, but there is a small
4284 * incompatibility so that the file cannot be used. Stop here
4285 * and signal success - the actual open will of course fail,
4286 * but that will create a really sensible error message. */
4287 || ( rc != VERR_VD_GEN_INVALID_HEADER
4288 && rc != VERR_VD_VDI_INVALID_HEADER
4289 && rc != VERR_VD_VMDK_INVALID_HEADER
4290 && rc != VERR_VD_ISCSI_INVALID_HEADER
4291 && rc != VERR_VD_VHD_INVALID_HEADER
4292 && rc != VERR_VD_RAW_INVALID_HEADER
4293 && rc != VERR_VD_PARALLELS_INVALID_HEADER
4294 && rc != VERR_VD_DMG_INVALID_HEADER))
4295 {
4296 /* Copy the name into the new string. */
4297 char *pszFormat = RTStrDup(g_apBackends[i]->pszBackendName);
4298 if (!pszFormat)
4299 {
4300 rc = VERR_NO_MEMORY;
4301 break;
4302 }
4303 *ppszFormat = pszFormat;
4304 rc = VINF_SUCCESS;
4305 break;
4306 }
4307 rc = VERR_NOT_SUPPORTED;
4308 }
4309 }
4310
4311 /* Try the cache backends. */
4312 if (rc == VERR_NOT_SUPPORTED)
4313 {
4314 for (unsigned i = 0; i < g_cCacheBackends; i++)
4315 {
4316 if (g_apCacheBackends[i]->pfnProbe)
4317 {
4318 rc = g_apCacheBackends[i]->pfnProbe(pszFilename, pVDIfsDisk,
4319 pVDIfsImage);
4320 if ( RT_SUCCESS(rc)
4321 || (rc != VERR_VD_GEN_INVALID_HEADER))
4322 {
4323 /* Copy the name into the new string. */
4324 char *pszFormat = RTStrDup(g_apBackends[i]->pszBackendName);
4325 if (!pszFormat)
4326 {
4327 rc = VERR_NO_MEMORY;
4328 break;
4329 }
4330 *ppszFormat = pszFormat;
4331 rc = VINF_SUCCESS;
4332 break;
4333 }
4334 rc = VERR_NOT_SUPPORTED;
4335 }
4336 }
4337 }
4338
4339 LogFlowFunc(("returns %Rrc *ppszFormat=\"%s\"\n", rc, *ppszFormat));
4340 return rc;
4341}
4342
4343/**
4344 * Opens an image file.
4345 *
4346 * The first opened image file in HDD container must have a base image type,
4347 * others (next opened images) must be a differencing or undo images.
4348 * Linkage is checked for differencing image to be in consistence with the previously opened image.
4349 * When another differencing image is opened and the last image was opened in read/write access
4350 * mode, then the last image is reopened in read-only with deny write sharing mode. This allows
4351 * other processes to use images in read-only mode too.
4352 *
4353 * Note that the image is opened in read-only mode if a read/write open is not possible.
4354 * Use VDIsReadOnly to check open mode.
4355 *
4356 * @returns VBox status code.
4357 * @param pDisk Pointer to HDD container.
4358 * @param pszBackend Name of the image file backend to use.
4359 * @param pszFilename Name of the image file to open.
4360 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
4361 * @param pVDIfsImage Pointer to the per-image VD interface list.
4362 */
4363VBOXDDU_DECL(int) VDOpen(PVBOXHDD pDisk, const char *pszBackend,
4364 const char *pszFilename, unsigned uOpenFlags,
4365 PVDINTERFACE pVDIfsImage)
4366{
4367 int rc = VINF_SUCCESS;
4368 int rc2;
4369 bool fLockWrite = false;
4370 PVDIMAGE pImage = NULL;
4371
4372 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x, pVDIfsImage=%#p\n",
4373 pDisk, pszBackend, pszFilename, uOpenFlags, pVDIfsImage));
4374
4375 do
4376 {
4377 /* sanity check */
4378 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4379 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4380
4381 /* Check arguments. */
4382 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
4383 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
4384 rc = VERR_INVALID_PARAMETER);
4385 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
4386 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
4387 rc = VERR_INVALID_PARAMETER);
4388 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
4389 ("uOpenFlags=%#x\n", uOpenFlags),
4390 rc = VERR_INVALID_PARAMETER);
4391
4392 /* Set up image descriptor. */
4393 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
4394 if (!pImage)
4395 {
4396 rc = VERR_NO_MEMORY;
4397 break;
4398 }
4399 pImage->pszFilename = RTStrDup(pszFilename);
4400 if (!pImage->pszFilename)
4401 {
4402 rc = VERR_NO_MEMORY;
4403 break;
4404 }
4405
4406 pImage->VDIo.pDisk = pDisk;
4407 pImage->pVDIfsImage = pVDIfsImage;
4408
4409 rc = vdFindBackend(pszBackend, &pImage->Backend);
4410 if (RT_FAILURE(rc))
4411 break;
4412 if (!pImage->Backend)
4413 {
4414 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4415 N_("VD: unknown backend name '%s'"), pszBackend);
4416 break;
4417 }
4418
4419 /*
4420 * Fail if the the backend can't do async I/O but the
4421 * flag is set.
4422 */
4423 if ( !(pImage->Backend->uBackendCaps & VD_CAP_ASYNC)
4424 && (uOpenFlags & VD_OPEN_FLAGS_ASYNC_IO))
4425 {
4426 rc = vdError(pDisk, VERR_NOT_SUPPORTED, RT_SRC_POS,
4427 N_("VD: Backend '%s' does not support async I/O"), pszBackend);
4428 break;
4429 }
4430
4431 /* Set up the I/O interface. */
4432 pImage->VDIo.pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
4433 if (pImage->VDIo.pInterfaceIO)
4434 pImage->VDIo.pInterfaceIOCallbacks = VDGetInterfaceIO(pImage->VDIo.pInterfaceIO);
4435 else
4436 {
4437 rc = VDInterfaceAdd(&pImage->VDIo.VDIIO, "VD_IO", VDINTERFACETYPE_IO,
4438 &pDisk->VDIIOCallbacks, pDisk, &pVDIfsImage);
4439 pImage->VDIo.pInterfaceIO = &pImage->VDIo.VDIIO;
4440 pImage->VDIo.pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
4441 }
4442
4443 /* Set up the internal I/O interface. */
4444 AssertBreakStmt(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
4445 rc = VERR_INVALID_PARAMETER);
4446 rc = VDInterfaceAdd(&pImage->VDIo.VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
4447 &pDisk->VDIIOIntCallbacks, &pImage->VDIo, &pImage->pVDIfsImage);
4448 AssertRC(rc);
4449
4450 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
4451 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
4452 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
4453 pDisk->pVDIfsDisk,
4454 pImage->pVDIfsImage,
4455 pDisk->enmType,
4456 &pImage->pBackendData);
4457 /* If the open in read-write mode failed, retry in read-only mode. */
4458 if (RT_FAILURE(rc))
4459 {
4460 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
4461 && ( rc == VERR_ACCESS_DENIED
4462 || rc == VERR_PERMISSION_DENIED
4463 || rc == VERR_WRITE_PROTECT
4464 || rc == VERR_SHARING_VIOLATION
4465 || rc == VERR_FILE_LOCK_FAILED))
4466 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
4467 (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
4468 | VD_OPEN_FLAGS_READONLY,
4469 pDisk->pVDIfsDisk,
4470 pImage->pVDIfsImage,
4471 pDisk->enmType,
4472 &pImage->pBackendData);
4473 if (RT_FAILURE(rc))
4474 {
4475 rc = vdError(pDisk, rc, RT_SRC_POS,
4476 N_("VD: error %Rrc opening image file '%s'"), rc, pszFilename);
4477 break;
4478 }
4479 }
4480
4481 /* Lock disk for writing, as we modify pDisk information below. */
4482 rc2 = vdThreadStartWrite(pDisk);
4483 AssertRC(rc2);
4484 fLockWrite = true;
4485
4486 pImage->VDIo.pBackendData = pImage->pBackendData;
4487
4488 /* Check image type. As the image itself has only partial knowledge
4489 * whether it's a base image or not, this info is derived here. The
4490 * base image can be fixed or normal, all others must be normal or
4491 * diff images. Some image formats don't distinguish between normal
4492 * and diff images, so this must be corrected here. */
4493 unsigned uImageFlags;
4494 uImageFlags = pImage->Backend->pfnGetImageFlags(pImage->pBackendData);
4495 if (RT_FAILURE(rc))
4496 uImageFlags = VD_IMAGE_FLAGS_NONE;
4497 if ( RT_SUCCESS(rc)
4498 && !(uOpenFlags & VD_OPEN_FLAGS_INFO))
4499 {
4500 if ( pDisk->cImages == 0
4501 && (uImageFlags & VD_IMAGE_FLAGS_DIFF))
4502 {
4503 rc = VERR_VD_INVALID_TYPE;
4504 break;
4505 }
4506 else if (pDisk->cImages != 0)
4507 {
4508 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
4509 {
4510 rc = VERR_VD_INVALID_TYPE;
4511 break;
4512 }
4513 else
4514 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
4515 }
4516 }
4517
4518 /* Ensure we always get correct diff information, even if the backend
4519 * doesn't actually have a stored flag for this. It must not return
4520 * bogus information for the parent UUID if it is not a diff image. */
4521 RTUUID parentUuid;
4522 RTUuidClear(&parentUuid);
4523 rc2 = pImage->Backend->pfnGetParentUuid(pImage->pBackendData, &parentUuid);
4524 if (RT_SUCCESS(rc2) && !RTUuidIsNull(&parentUuid))
4525 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
4526
4527 pImage->uImageFlags = uImageFlags;
4528
4529 /* Force sane optimization settings. It's not worth avoiding writes
4530 * to fixed size images. The overhead would have almost no payback. */
4531 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
4532 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
4533
4534 /** @todo optionally check UUIDs */
4535
4536 /* Cache disk information. */
4537 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
4538
4539 /* Cache PCHS geometry. */
4540 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
4541 &pDisk->PCHSGeometry);
4542 if (RT_FAILURE(rc2))
4543 {
4544 pDisk->PCHSGeometry.cCylinders = 0;
4545 pDisk->PCHSGeometry.cHeads = 0;
4546 pDisk->PCHSGeometry.cSectors = 0;
4547 }
4548 else
4549 {
4550 /* Make sure the PCHS geometry is properly clipped. */
4551 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
4552 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
4553 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
4554 }
4555
4556 /* Cache LCHS geometry. */
4557 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
4558 &pDisk->LCHSGeometry);
4559 if (RT_FAILURE(rc2))
4560 {
4561 pDisk->LCHSGeometry.cCylinders = 0;
4562 pDisk->LCHSGeometry.cHeads = 0;
4563 pDisk->LCHSGeometry.cSectors = 0;
4564 }
4565 else
4566 {
4567 /* Make sure the LCHS geometry is properly clipped. */
4568 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
4569 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
4570 }
4571
4572 if (pDisk->cImages != 0)
4573 {
4574 /* Switch previous image to read-only mode. */
4575 unsigned uOpenFlagsPrevImg;
4576 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
4577 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
4578 {
4579 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
4580 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pBackendData, uOpenFlagsPrevImg);
4581 }
4582 }
4583
4584 if (RT_SUCCESS(rc))
4585 {
4586 /* Image successfully opened, make it the last image. */
4587 vdAddImageToList(pDisk, pImage);
4588 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
4589 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
4590 }
4591 else
4592 {
4593 /* Error detected, but image opened. Close image. */
4594 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, false);
4595 AssertRC(rc2);
4596 pImage->pBackendData = NULL;
4597 }
4598 } while (0);
4599
4600 if (RT_UNLIKELY(fLockWrite))
4601 {
4602 rc2 = vdThreadFinishWrite(pDisk);
4603 AssertRC(rc2);
4604 }
4605
4606 if (RT_FAILURE(rc))
4607 {
4608 if (pImage)
4609 {
4610 if (pImage->pszFilename)
4611 RTStrFree(pImage->pszFilename);
4612 RTMemFree(pImage);
4613 }
4614 }
4615
4616 LogFlowFunc(("returns %Rrc\n", rc));
4617 return rc;
4618}
4619
4620/**
4621 * Opens a cache image.
4622 *
4623 * @return VBox status code.
4624 * @param pDisk Pointer to the HDD container which should use the cache image.
4625 * @param pszBackend Name of the cache file backend to use (case insensitive).
4626 * @param pszFilename Name of the cache image to open.
4627 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
4628 * @param pVDIfsCache Pointer to the per-cache VD interface list.
4629 */
4630VBOXDDU_DECL(int) VDCacheOpen(PVBOXHDD pDisk, const char *pszBackend,
4631 const char *pszFilename, unsigned uOpenFlags,
4632 PVDINTERFACE pVDIfsCache)
4633{
4634 int rc = VINF_SUCCESS;
4635 int rc2;
4636 bool fLockWrite = false;
4637 PVDCACHE pCache = NULL;
4638
4639 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x, pVDIfsCache=%#p\n",
4640 pDisk, pszBackend, pszFilename, uOpenFlags, pVDIfsCache));
4641
4642 do
4643 {
4644 /* sanity check */
4645 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4646 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4647
4648 /* Check arguments. */
4649 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
4650 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
4651 rc = VERR_INVALID_PARAMETER);
4652 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
4653 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
4654 rc = VERR_INVALID_PARAMETER);
4655 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
4656 ("uOpenFlags=%#x\n", uOpenFlags),
4657 rc = VERR_INVALID_PARAMETER);
4658
4659 /* Set up image descriptor. */
4660 pCache = (PVDCACHE)RTMemAllocZ(sizeof(VDCACHE));
4661 if (!pCache)
4662 {
4663 rc = VERR_NO_MEMORY;
4664 break;
4665 }
4666 pCache->pszFilename = RTStrDup(pszFilename);
4667 if (!pCache->pszFilename)
4668 {
4669 rc = VERR_NO_MEMORY;
4670 break;
4671 }
4672
4673 pCache->VDIo.pDisk = pDisk;
4674 pCache->pVDIfsCache = pVDIfsCache;
4675
4676 rc = vdFindCacheBackend(pszBackend, &pCache->Backend);
4677 if (RT_FAILURE(rc))
4678 break;
4679 if (!pCache->Backend)
4680 {
4681 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4682 N_("VD: unknown backend name '%s'"), pszBackend);
4683 break;
4684 }
4685
4686 /* Set up the I/O interface. */
4687 pCache->VDIo.pInterfaceIO = VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IO);
4688 if (pCache->VDIo.pInterfaceIO)
4689 pCache->VDIo.pInterfaceIOCallbacks = VDGetInterfaceIO(pCache->VDIo.pInterfaceIO);
4690 else
4691 {
4692 rc = VDInterfaceAdd(&pCache->VDIo.VDIIO, "VD_IO", VDINTERFACETYPE_IO,
4693 &pDisk->VDIIOCallbacks, pDisk, &pVDIfsCache);
4694 pCache->VDIo.pInterfaceIO = &pCache->VDIo.VDIIO;
4695 pCache->VDIo.pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
4696 }
4697
4698 /* Set up the internal I/O interface. */
4699 AssertBreakStmt(!VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IOINT),
4700 rc = VERR_INVALID_PARAMETER);
4701 rc = VDInterfaceAdd(&pCache->VDIo.VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
4702 &pDisk->VDIIOIntCallbacks, &pCache->VDIo, &pCache->pVDIfsCache);
4703 AssertRC(rc);
4704
4705 pCache->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
4706 rc = pCache->Backend->pfnOpen(pCache->pszFilename,
4707 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
4708 pDisk->pVDIfsDisk,
4709 pCache->pVDIfsCache,
4710 &pCache->pBackendData);
4711 /* If the open in read-write mode failed, retry in read-only mode. */
4712 if (RT_FAILURE(rc))
4713 {
4714 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
4715 && ( rc == VERR_ACCESS_DENIED
4716 || rc == VERR_PERMISSION_DENIED
4717 || rc == VERR_WRITE_PROTECT
4718 || rc == VERR_SHARING_VIOLATION
4719 || rc == VERR_FILE_LOCK_FAILED))
4720 rc = pCache->Backend->pfnOpen(pCache->pszFilename,
4721 (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
4722 | VD_OPEN_FLAGS_READONLY,
4723 pDisk->pVDIfsDisk,
4724 pCache->pVDIfsCache,
4725 &pCache->pBackendData);
4726 if (RT_FAILURE(rc))
4727 {
4728 rc = vdError(pDisk, rc, RT_SRC_POS,
4729 N_("VD: error %Rrc opening image file '%s'"), rc, pszFilename);
4730 break;
4731 }
4732 }
4733
4734 /* Lock disk for writing, as we modify pDisk information below. */
4735 rc2 = vdThreadStartWrite(pDisk);
4736 AssertRC(rc2);
4737 fLockWrite = true;
4738
4739 /*
4740 * Check that the modification UUID of the cache and last image
4741 * match. If not the image was modified in-between without the cache.
4742 * The cache might contain stale data.
4743 */
4744 RTUUID UuidImage, UuidCache;
4745
4746 rc = pCache->Backend->pfnGetModificationUuid(pCache->pBackendData,
4747 &UuidCache);
4748 if (RT_SUCCESS(rc))
4749 {
4750 rc = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
4751 &UuidImage);
4752 if (RT_SUCCESS(rc))
4753 {
4754 if (RTUuidCompare(&UuidImage, &UuidCache))
4755 rc = VERR_VD_CACHE_NOT_UP_TO_DATE;
4756 }
4757 }
4758
4759 /*
4760 * We assume that the user knows what he is doing if one of the images
4761 * doesn't support the modification uuid.
4762 */
4763 if (rc == VERR_NOT_SUPPORTED)
4764 rc = VINF_SUCCESS;
4765
4766 if (RT_SUCCESS(rc))
4767 {
4768 /* Cache successfully opened, make it the current one. */
4769 if (!pDisk->pCache)
4770 pDisk->pCache = pCache;
4771 else
4772 rc = VERR_VD_CACHE_ALREADY_EXISTS;
4773 }
4774
4775 if (RT_FAILURE(rc))
4776 {
4777 /* Error detected, but image opened. Close image. */
4778 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, false);
4779 AssertRC(rc2);
4780 pCache->pBackendData = NULL;
4781 }
4782 } while (0);
4783
4784 if (RT_UNLIKELY(fLockWrite))
4785 {
4786 rc2 = vdThreadFinishWrite(pDisk);
4787 AssertRC(rc2);
4788 }
4789
4790 if (RT_FAILURE(rc))
4791 {
4792 if (pCache)
4793 {
4794 if (pCache->pszFilename)
4795 RTStrFree(pCache->pszFilename);
4796 RTMemFree(pCache);
4797 }
4798 }
4799
4800 LogFlowFunc(("returns %Rrc\n", rc));
4801 return rc;
4802}
4803
4804/**
4805 * Creates and opens a new base image file.
4806 *
4807 * @returns VBox status code.
4808 * @param pDisk Pointer to HDD container.
4809 * @param pszBackend Name of the image file backend to use.
4810 * @param pszFilename Name of the image file to create.
4811 * @param cbSize Image size in bytes.
4812 * @param uImageFlags Flags specifying special image features.
4813 * @param pszComment Pointer to image comment. NULL is ok.
4814 * @param pPCHSGeometry Pointer to physical disk geometry <= (16383,16,63). Not NULL.
4815 * @param pLCHSGeometry Pointer to logical disk geometry <= (x,255,63). Not NULL.
4816 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
4817 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
4818 * @param pVDIfsImage Pointer to the per-image VD interface list.
4819 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
4820 */
4821VBOXDDU_DECL(int) VDCreateBase(PVBOXHDD pDisk, const char *pszBackend,
4822 const char *pszFilename, uint64_t cbSize,
4823 unsigned uImageFlags, const char *pszComment,
4824 PCVDGEOMETRY pPCHSGeometry,
4825 PCVDGEOMETRY pLCHSGeometry,
4826 PCRTUUID pUuid, unsigned uOpenFlags,
4827 PVDINTERFACE pVDIfsImage,
4828 PVDINTERFACE pVDIfsOperation)
4829{
4830 int rc = VINF_SUCCESS;
4831 int rc2;
4832 bool fLockWrite = false, fLockRead = false;
4833 PVDIMAGE pImage = NULL;
4834 RTUUID uuid;
4835
4836 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",
4837 pDisk, pszBackend, pszFilename, cbSize, uImageFlags, pszComment,
4838 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
4839 pPCHSGeometry->cSectors, pLCHSGeometry->cCylinders,
4840 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors, pUuid,
4841 uOpenFlags, pVDIfsImage, pVDIfsOperation));
4842
4843 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
4844 VDINTERFACETYPE_PROGRESS);
4845 PVDINTERFACEPROGRESS pCbProgress = NULL;
4846 if (pIfProgress)
4847 pCbProgress = VDGetInterfaceProgress(pIfProgress);
4848
4849 do
4850 {
4851 /* sanity check */
4852 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4853 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4854
4855 /* Check arguments. */
4856 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
4857 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
4858 rc = VERR_INVALID_PARAMETER);
4859 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
4860 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
4861 rc = VERR_INVALID_PARAMETER);
4862 AssertMsgBreakStmt(cbSize,
4863 ("cbSize=%llu\n", cbSize),
4864 rc = VERR_INVALID_PARAMETER);
4865 AssertMsgBreakStmt( ((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0)
4866 || ((uImageFlags & (VD_IMAGE_FLAGS_FIXED | VD_IMAGE_FLAGS_DIFF)) != VD_IMAGE_FLAGS_FIXED),
4867 ("uImageFlags=%#x\n", uImageFlags),
4868 rc = VERR_INVALID_PARAMETER);
4869 /* The PCHS geometry fields may be 0 to leave it for later. */
4870 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
4871 && pPCHSGeometry->cHeads <= 16
4872 && pPCHSGeometry->cSectors <= 63,
4873 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
4874 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
4875 pPCHSGeometry->cSectors),
4876 rc = VERR_INVALID_PARAMETER);
4877 /* The LCHS geometry fields may be 0 to leave it to later autodetection. */
4878 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
4879 && pLCHSGeometry->cHeads <= 255
4880 && pLCHSGeometry->cSectors <= 63,
4881 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
4882 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
4883 pLCHSGeometry->cSectors),
4884 rc = VERR_INVALID_PARAMETER);
4885 /* The UUID may be NULL. */
4886 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
4887 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
4888 rc = VERR_INVALID_PARAMETER);
4889 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
4890 ("uOpenFlags=%#x\n", uOpenFlags),
4891 rc = VERR_INVALID_PARAMETER);
4892
4893 /* Check state. Needs a temporary read lock. Holding the write lock
4894 * all the time would be blocking other activities for too long. */
4895 rc2 = vdThreadStartRead(pDisk);
4896 AssertRC(rc2);
4897 fLockRead = true;
4898 AssertMsgBreakStmt(pDisk->cImages == 0,
4899 ("Create base image cannot be done with other images open\n"),
4900 rc = VERR_VD_INVALID_STATE);
4901 rc2 = vdThreadFinishRead(pDisk);
4902 AssertRC(rc2);
4903 fLockRead = false;
4904
4905 /* Set up image descriptor. */
4906 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
4907 if (!pImage)
4908 {
4909 rc = VERR_NO_MEMORY;
4910 break;
4911 }
4912 pImage->pszFilename = RTStrDup(pszFilename);
4913 if (!pImage->pszFilename)
4914 {
4915 rc = VERR_NO_MEMORY;
4916 break;
4917 }
4918 pImage->VDIo.pDisk = pDisk;
4919 pImage->pVDIfsImage = pVDIfsImage;
4920
4921 /* Set up the I/O interface. */
4922 pImage->VDIo.pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
4923 if (pImage->VDIo.pInterfaceIO)
4924 pImage->VDIo.pInterfaceIOCallbacks = VDGetInterfaceIO(pImage->VDIo.pInterfaceIO);
4925 else
4926 {
4927 rc = VDInterfaceAdd(&pImage->VDIo.VDIIO, "VD_IO", VDINTERFACETYPE_IO,
4928 &pDisk->VDIIOCallbacks, pDisk, &pVDIfsImage);
4929 pImage->VDIo.pInterfaceIO = &pImage->VDIo.VDIIO;
4930 pImage->VDIo.pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
4931 }
4932
4933 /* Set up the internal I/O interface. */
4934 AssertBreakStmt(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
4935 rc = VERR_INVALID_PARAMETER);
4936 rc = VDInterfaceAdd(&pImage->VDIo.VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
4937 &pDisk->VDIIOIntCallbacks, &pImage->VDIo, &pImage->pVDIfsImage);
4938 AssertRC(rc);
4939
4940 rc = vdFindBackend(pszBackend, &pImage->Backend);
4941 if (RT_FAILURE(rc))
4942 break;
4943 if (!pImage->Backend)
4944 {
4945 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4946 N_("VD: unknown backend name '%s'"), pszBackend);
4947 break;
4948 }
4949 if (!(pImage->Backend->uBackendCaps & ( VD_CAP_CREATE_FIXED
4950 | VD_CAP_CREATE_DYNAMIC)))
4951 {
4952 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4953 N_("VD: backend '%s' cannot create base images"), pszBackend);
4954 break;
4955 }
4956
4957 /* Create UUID if the caller didn't specify one. */
4958 if (!pUuid)
4959 {
4960 rc = RTUuidCreate(&uuid);
4961 if (RT_FAILURE(rc))
4962 {
4963 rc = vdError(pDisk, rc, RT_SRC_POS,
4964 N_("VD: cannot generate UUID for image '%s'"),
4965 pszFilename);
4966 break;
4967 }
4968 pUuid = &uuid;
4969 }
4970
4971 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
4972 uImageFlags &= ~VD_IMAGE_FLAGS_DIFF;
4973 rc = pImage->Backend->pfnCreate(pImage->pszFilename, cbSize,
4974 uImageFlags, pszComment, pPCHSGeometry,
4975 pLCHSGeometry, pUuid,
4976 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
4977 0, 99,
4978 pDisk->pVDIfsDisk,
4979 pImage->pVDIfsImage,
4980 pVDIfsOperation,
4981 &pImage->pBackendData);
4982
4983 if (RT_SUCCESS(rc))
4984 {
4985 pImage->VDIo.pBackendData = pImage->pBackendData;
4986 pImage->uImageFlags = uImageFlags;
4987
4988 /* Force sane optimization settings. It's not worth avoiding writes
4989 * to fixed size images. The overhead would have almost no payback. */
4990 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
4991 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
4992
4993 /* Lock disk for writing, as we modify pDisk information below. */
4994 rc2 = vdThreadStartWrite(pDisk);
4995 AssertRC(rc2);
4996 fLockWrite = true;
4997
4998 /** @todo optionally check UUIDs */
4999
5000 /* Re-check state, as the lock wasn't held and another image
5001 * creation call could have been done by another thread. */
5002 AssertMsgStmt(pDisk->cImages == 0,
5003 ("Create base image cannot be done with other images open\n"),
5004 rc = VERR_VD_INVALID_STATE);
5005 }
5006
5007 if (RT_SUCCESS(rc))
5008 {
5009 /* Cache disk information. */
5010 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
5011
5012 /* Cache PCHS geometry. */
5013 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
5014 &pDisk->PCHSGeometry);
5015 if (RT_FAILURE(rc2))
5016 {
5017 pDisk->PCHSGeometry.cCylinders = 0;
5018 pDisk->PCHSGeometry.cHeads = 0;
5019 pDisk->PCHSGeometry.cSectors = 0;
5020 }
5021 else
5022 {
5023 /* Make sure the CHS geometry is properly clipped. */
5024 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
5025 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
5026 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
5027 }
5028
5029 /* Cache LCHS geometry. */
5030 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
5031 &pDisk->LCHSGeometry);
5032 if (RT_FAILURE(rc2))
5033 {
5034 pDisk->LCHSGeometry.cCylinders = 0;
5035 pDisk->LCHSGeometry.cHeads = 0;
5036 pDisk->LCHSGeometry.cSectors = 0;
5037 }
5038 else
5039 {
5040 /* Make sure the CHS geometry is properly clipped. */
5041 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
5042 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
5043 }
5044
5045 /* Image successfully opened, make it the last image. */
5046 vdAddImageToList(pDisk, pImage);
5047 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
5048 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
5049 }
5050 else
5051 {
5052 /* Error detected, image may or may not be opened. Close and delete
5053 * image if it was opened. */
5054 if (pImage->pBackendData)
5055 {
5056 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, true);
5057 AssertRC(rc2);
5058 pImage->pBackendData = NULL;
5059 }
5060 }
5061 } while (0);
5062
5063 if (RT_UNLIKELY(fLockWrite))
5064 {
5065 rc2 = vdThreadFinishWrite(pDisk);
5066 AssertRC(rc2);
5067 }
5068 else if (RT_UNLIKELY(fLockRead))
5069 {
5070 rc2 = vdThreadFinishRead(pDisk);
5071 AssertRC(rc2);
5072 }
5073
5074 if (RT_FAILURE(rc))
5075 {
5076 if (pImage)
5077 {
5078 if (pImage->pszFilename)
5079 RTStrFree(pImage->pszFilename);
5080 RTMemFree(pImage);
5081 }
5082 }
5083
5084 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
5085 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
5086
5087 LogFlowFunc(("returns %Rrc\n", rc));
5088 return rc;
5089}
5090
5091/**
5092 * Creates and opens a new differencing image file in HDD container.
5093 * See comments for VDOpen function about differencing images.
5094 *
5095 * @returns VBox status code.
5096 * @param pDisk Pointer to HDD container.
5097 * @param pszBackend Name of the image file backend to use.
5098 * @param pszFilename Name of the differencing image file to create.
5099 * @param uImageFlags Flags specifying special image features.
5100 * @param pszComment Pointer to image comment. NULL is ok.
5101 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
5102 * @param pParentUuid New parent UUID of the image. If NULL, the UUID is queried automatically.
5103 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
5104 * @param pVDIfsImage Pointer to the per-image VD interface list.
5105 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
5106 */
5107VBOXDDU_DECL(int) VDCreateDiff(PVBOXHDD pDisk, const char *pszBackend,
5108 const char *pszFilename, unsigned uImageFlags,
5109 const char *pszComment, PCRTUUID pUuid,
5110 PCRTUUID pParentUuid, unsigned uOpenFlags,
5111 PVDINTERFACE pVDIfsImage,
5112 PVDINTERFACE pVDIfsOperation)
5113{
5114 int rc = VINF_SUCCESS;
5115 int rc2;
5116 bool fLockWrite = false, fLockRead = false;
5117 PVDIMAGE pImage = NULL;
5118 RTUUID uuid;
5119
5120 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
5121 pDisk, pszBackend, pszFilename, uImageFlags, pszComment, pUuid, uOpenFlags, pVDIfsImage, pVDIfsOperation));
5122
5123 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
5124 VDINTERFACETYPE_PROGRESS);
5125 PVDINTERFACEPROGRESS pCbProgress = NULL;
5126 if (pIfProgress)
5127 pCbProgress = VDGetInterfaceProgress(pIfProgress);
5128
5129 do
5130 {
5131 /* sanity check */
5132 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
5133 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5134
5135 /* Check arguments. */
5136 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
5137 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
5138 rc = VERR_INVALID_PARAMETER);
5139 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
5140 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
5141 rc = VERR_INVALID_PARAMETER);
5142 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
5143 ("uImageFlags=%#x\n", uImageFlags),
5144 rc = VERR_INVALID_PARAMETER);
5145 /* The UUID may be NULL. */
5146 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
5147 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
5148 rc = VERR_INVALID_PARAMETER);
5149 /* The parent UUID may be NULL. */
5150 AssertMsgBreakStmt(pParentUuid == NULL || VALID_PTR(pParentUuid),
5151 ("pParentUuid=%#p ParentUUID=%RTuuid\n", pParentUuid, pParentUuid),
5152 rc = VERR_INVALID_PARAMETER);
5153 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
5154 ("uOpenFlags=%#x\n", uOpenFlags),
5155 rc = VERR_INVALID_PARAMETER);
5156
5157 /* Check state. Needs a temporary read lock. Holding the write lock
5158 * all the time would be blocking other activities for too long. */
5159 rc2 = vdThreadStartRead(pDisk);
5160 AssertRC(rc2);
5161 fLockRead = true;
5162 AssertMsgBreakStmt(pDisk->cImages != 0,
5163 ("Create diff image cannot be done without other images open\n"),
5164 rc = VERR_VD_INVALID_STATE);
5165 rc2 = vdThreadFinishRead(pDisk);
5166 AssertRC(rc2);
5167 fLockRead = false;
5168
5169 /* Set up image descriptor. */
5170 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
5171 if (!pImage)
5172 {
5173 rc = VERR_NO_MEMORY;
5174 break;
5175 }
5176 pImage->pszFilename = RTStrDup(pszFilename);
5177 if (!pImage->pszFilename)
5178 {
5179 rc = VERR_NO_MEMORY;
5180 break;
5181 }
5182
5183 rc = vdFindBackend(pszBackend, &pImage->Backend);
5184 if (RT_FAILURE(rc))
5185 break;
5186 if (!pImage->Backend)
5187 {
5188 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
5189 N_("VD: unknown backend name '%s'"), pszBackend);
5190 break;
5191 }
5192 if ( !(pImage->Backend->uBackendCaps & VD_CAP_DIFF)
5193 || !(pImage->Backend->uBackendCaps & ( VD_CAP_CREATE_FIXED
5194 | VD_CAP_CREATE_DYNAMIC)))
5195 {
5196 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
5197 N_("VD: backend '%s' cannot create diff images"), pszBackend);
5198 break;
5199 }
5200
5201 pImage->VDIo.pDisk = pDisk;
5202 pImage->pVDIfsImage = pVDIfsImage;
5203
5204 /* Set up the I/O interface. */
5205 pImage->VDIo.pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
5206 if (pImage->VDIo.pInterfaceIO)
5207 pImage->VDIo.pInterfaceIOCallbacks = VDGetInterfaceIO(pImage->VDIo.pInterfaceIO);
5208 else
5209 {
5210 rc = VDInterfaceAdd(&pImage->VDIo.VDIIO, "VD_IO", VDINTERFACETYPE_IO,
5211 &pDisk->VDIIOCallbacks, pDisk, &pVDIfsImage);
5212 pImage->VDIo.pInterfaceIO = &pImage->VDIo.VDIIO;
5213 pImage->VDIo.pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
5214 }
5215
5216 /* Set up the internal I/O interface. */
5217 AssertBreakStmt(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
5218 rc = VERR_INVALID_PARAMETER);
5219 rc = VDInterfaceAdd(&pImage->VDIo.VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
5220 &pDisk->VDIIOIntCallbacks, &pImage->VDIo, &pImage->pVDIfsImage);
5221 AssertRC(rc);
5222
5223 /* Create UUID if the caller didn't specify one. */
5224 if (!pUuid)
5225 {
5226 rc = RTUuidCreate(&uuid);
5227 if (RT_FAILURE(rc))
5228 {
5229 rc = vdError(pDisk, rc, RT_SRC_POS,
5230 N_("VD: cannot generate UUID for image '%s'"),
5231 pszFilename);
5232 break;
5233 }
5234 pUuid = &uuid;
5235 }
5236
5237 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
5238 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
5239 rc = pImage->Backend->pfnCreate(pImage->pszFilename, pDisk->cbSize,
5240 uImageFlags | VD_IMAGE_FLAGS_DIFF,
5241 pszComment, &pDisk->PCHSGeometry,
5242 &pDisk->LCHSGeometry, pUuid,
5243 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
5244 0, 99,
5245 pDisk->pVDIfsDisk,
5246 pImage->pVDIfsImage,
5247 pVDIfsOperation,
5248 &pImage->pBackendData);
5249
5250 if (RT_SUCCESS(rc))
5251 {
5252 pImage->VDIo.pBackendData = pImage->pBackendData;
5253 pImage->uImageFlags = uImageFlags;
5254
5255 /* Lock disk for writing, as we modify pDisk information below. */
5256 rc2 = vdThreadStartWrite(pDisk);
5257 AssertRC(rc2);
5258 fLockWrite = true;
5259
5260 /* Switch previous image to read-only mode. */
5261 unsigned uOpenFlagsPrevImg;
5262 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
5263 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
5264 {
5265 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
5266 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pBackendData, uOpenFlagsPrevImg);
5267 }
5268
5269 /** @todo optionally check UUIDs */
5270
5271 /* Re-check state, as the lock wasn't held and another image
5272 * creation call could have been done by another thread. */
5273 AssertMsgStmt(pDisk->cImages != 0,
5274 ("Create diff image cannot be done without other images open\n"),
5275 rc = VERR_VD_INVALID_STATE);
5276 }
5277
5278 if (RT_SUCCESS(rc))
5279 {
5280 RTUUID Uuid;
5281 RTTIMESPEC ts;
5282
5283 if (pParentUuid && !RTUuidIsNull(pParentUuid))
5284 {
5285 Uuid = *pParentUuid;
5286 pImage->Backend->pfnSetParentUuid(pImage->pBackendData, &Uuid);
5287 }
5288 else
5289 {
5290 rc2 = pDisk->pLast->Backend->pfnGetUuid(pDisk->pLast->pBackendData,
5291 &Uuid);
5292 if (RT_SUCCESS(rc2))
5293 pImage->Backend->pfnSetParentUuid(pImage->pBackendData, &Uuid);
5294 }
5295 rc2 = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
5296 &Uuid);
5297 if (RT_SUCCESS(rc2))
5298 pImage->Backend->pfnSetParentModificationUuid(pImage->pBackendData,
5299 &Uuid);
5300 if (pDisk->pLast->Backend->pfnGetTimeStamp)
5301 rc2 = pDisk->pLast->Backend->pfnGetTimeStamp(pDisk->pLast->pBackendData,
5302 &ts);
5303 else
5304 rc2 = VERR_NOT_IMPLEMENTED;
5305 if (RT_SUCCESS(rc2) && pImage->Backend->pfnSetParentTimeStamp)
5306 pImage->Backend->pfnSetParentTimeStamp(pImage->pBackendData, &ts);
5307
5308 if (pImage->Backend->pfnSetParentFilename)
5309 rc2 = pImage->Backend->pfnSetParentFilename(pImage->pBackendData, pDisk->pLast->pszFilename);
5310 }
5311
5312 if (RT_SUCCESS(rc))
5313 {
5314 /* Image successfully opened, make it the last image. */
5315 vdAddImageToList(pDisk, pImage);
5316 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
5317 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
5318 }
5319 else
5320 {
5321 /* Error detected, but image opened. Close and delete image. */
5322 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, true);
5323 AssertRC(rc2);
5324 pImage->pBackendData = NULL;
5325 }
5326 } while (0);
5327
5328 if (RT_UNLIKELY(fLockWrite))
5329 {
5330 rc2 = vdThreadFinishWrite(pDisk);
5331 AssertRC(rc2);
5332 }
5333 else if (RT_UNLIKELY(fLockRead))
5334 {
5335 rc2 = vdThreadFinishRead(pDisk);
5336 AssertRC(rc2);
5337 }
5338
5339 if (RT_FAILURE(rc))
5340 {
5341 if (pImage)
5342 {
5343 if (pImage->pszFilename)
5344 RTStrFree(pImage->pszFilename);
5345 RTMemFree(pImage);
5346 }
5347 }
5348
5349 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
5350 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
5351
5352 LogFlowFunc(("returns %Rrc\n", rc));
5353 return rc;
5354}
5355
5356
5357/**
5358 * Creates and opens new cache image file in HDD container.
5359 *
5360 * @return VBox status code.
5361 * @param pDisk Name of the cache file backend to use (case insensitive).
5362 * @param pszFilename Name of the differencing cache file to create.
5363 * @param cbSize Maximum size of the cache.
5364 * @param uImageFlags Flags specifying special cache features.
5365 * @param pszComment Pointer to image comment. NULL is ok.
5366 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
5367 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
5368 * @param pVDIfsCache Pointer to the per-cache VD interface list.
5369 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
5370 */
5371VBOXDDU_DECL(int) VDCreateCache(PVBOXHDD pDisk, const char *pszBackend,
5372 const char *pszFilename, uint64_t cbSize,
5373 unsigned uImageFlags, const char *pszComment,
5374 PCRTUUID pUuid, unsigned uOpenFlags,
5375 PVDINTERFACE pVDIfsCache, PVDINTERFACE pVDIfsOperation)
5376{
5377 int rc = VINF_SUCCESS;
5378 int rc2;
5379 bool fLockWrite = false, fLockRead = false;
5380 PVDCACHE pCache = NULL;
5381 RTUUID uuid;
5382
5383 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
5384 pDisk, pszBackend, pszFilename, cbSize, uImageFlags, pszComment, pUuid, uOpenFlags, pVDIfsCache, pVDIfsOperation));
5385
5386 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
5387 VDINTERFACETYPE_PROGRESS);
5388 PVDINTERFACEPROGRESS pCbProgress = NULL;
5389 if (pIfProgress)
5390 pCbProgress = VDGetInterfaceProgress(pIfProgress);
5391
5392 do
5393 {
5394 /* sanity check */
5395 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
5396 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5397
5398 /* Check arguments. */
5399 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
5400 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
5401 rc = VERR_INVALID_PARAMETER);
5402 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
5403 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
5404 rc = VERR_INVALID_PARAMETER);
5405 AssertMsgBreakStmt(cbSize,
5406 ("cbSize=%llu\n", cbSize),
5407 rc = VERR_INVALID_PARAMETER);
5408 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
5409 ("uImageFlags=%#x\n", uImageFlags),
5410 rc = VERR_INVALID_PARAMETER);
5411 /* The UUID may be NULL. */
5412 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
5413 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
5414 rc = VERR_INVALID_PARAMETER);
5415 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
5416 ("uOpenFlags=%#x\n", uOpenFlags),
5417 rc = VERR_INVALID_PARAMETER);
5418
5419 /* Check state. Needs a temporary read lock. Holding the write lock
5420 * all the time would be blocking other activities for too long. */
5421 rc2 = vdThreadStartRead(pDisk);
5422 AssertRC(rc2);
5423 fLockRead = true;
5424 AssertMsgBreakStmt(!pDisk->pCache,
5425 ("Create cache image cannot be done with a cache already attached\n"),
5426 rc = VERR_VD_CACHE_ALREADY_EXISTS);
5427 rc2 = vdThreadFinishRead(pDisk);
5428 AssertRC(rc2);
5429 fLockRead = false;
5430
5431 /* Set up image descriptor. */
5432 pCache = (PVDCACHE)RTMemAllocZ(sizeof(VDCACHE));
5433 if (!pCache)
5434 {
5435 rc = VERR_NO_MEMORY;
5436 break;
5437 }
5438 pCache->pszFilename = RTStrDup(pszFilename);
5439 if (!pCache->pszFilename)
5440 {
5441 rc = VERR_NO_MEMORY;
5442 break;
5443 }
5444
5445 rc = vdFindCacheBackend(pszBackend, &pCache->Backend);
5446 if (RT_FAILURE(rc))
5447 break;
5448 if (!pCache->Backend)
5449 {
5450 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
5451 N_("VD: unknown backend name '%s'"), pszBackend);
5452 break;
5453 }
5454
5455 pCache->VDIo.pDisk = pDisk;
5456 pCache->pVDIfsCache = pVDIfsCache;
5457
5458 /* Set up the I/O interface. */
5459 pCache->VDIo.pInterfaceIO = VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IO);
5460 if (pCache->VDIo.pInterfaceIO)
5461 pCache->VDIo.pInterfaceIOCallbacks = VDGetInterfaceIO(pCache->VDIo.pInterfaceIO);
5462 else
5463 {
5464 rc = VDInterfaceAdd(&pCache->VDIo.VDIIO, "VD_IO", VDINTERFACETYPE_IO,
5465 &pDisk->VDIIOCallbacks, pDisk, &pVDIfsCache);
5466 pCache->VDIo.pInterfaceIO = &pCache->VDIo.VDIIO;
5467 pCache->VDIo.pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
5468 }
5469
5470 /* Set up the internal I/O interface. */
5471 AssertBreakStmt(!VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IOINT),
5472 rc = VERR_INVALID_PARAMETER);
5473 rc = VDInterfaceAdd(&pCache->VDIo.VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
5474 &pDisk->VDIIOIntCallbacks, &pCache->VDIo, &pCache->pVDIfsCache);
5475 AssertRC(rc);
5476
5477 /* Create UUID if the caller didn't specify one. */
5478 if (!pUuid)
5479 {
5480 rc = RTUuidCreate(&uuid);
5481 if (RT_FAILURE(rc))
5482 {
5483 rc = vdError(pDisk, rc, RT_SRC_POS,
5484 N_("VD: cannot generate UUID for image '%s'"),
5485 pszFilename);
5486 break;
5487 }
5488 pUuid = &uuid;
5489 }
5490
5491 pCache->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
5492 rc = pCache->Backend->pfnCreate(pCache->pszFilename, cbSize,
5493 uImageFlags,
5494 pszComment, pUuid,
5495 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
5496 0, 99,
5497 pDisk->pVDIfsDisk,
5498 pCache->pVDIfsCache,
5499 pVDIfsOperation,
5500 &pCache->pBackendData);
5501
5502 if (RT_SUCCESS(rc))
5503 {
5504 /* Lock disk for writing, as we modify pDisk information below. */
5505 rc2 = vdThreadStartWrite(pDisk);
5506 AssertRC(rc2);
5507 fLockWrite = true;
5508
5509 pCache->VDIo.pBackendData = pCache->pBackendData;
5510
5511 /* Re-check state, as the lock wasn't held and another image
5512 * creation call could have been done by another thread. */
5513 AssertMsgStmt(!pDisk->pCache,
5514 ("Create cache image cannot be done with another cache open\n"),
5515 rc = VERR_VD_CACHE_ALREADY_EXISTS);
5516 }
5517
5518 if ( RT_SUCCESS(rc)
5519 && pDisk->pLast)
5520 {
5521 RTUUID UuidModification;
5522
5523 /* Set same modification Uuid as the last image. */
5524 rc = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
5525 &UuidModification);
5526 if (RT_SUCCESS(rc))
5527 {
5528 rc = pCache->Backend->pfnSetModificationUuid(pCache->pBackendData,
5529 &UuidModification);
5530 }
5531
5532 if (rc == VERR_NOT_SUPPORTED)
5533 rc = VINF_SUCCESS;
5534 }
5535
5536 if (RT_SUCCESS(rc))
5537 {
5538 /* Cache successfully created. */
5539 pDisk->pCache = pCache;
5540 }
5541 else
5542 {
5543 /* Error detected, but image opened. Close and delete image. */
5544 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, true);
5545 AssertRC(rc2);
5546 pCache->pBackendData = NULL;
5547 }
5548 } while (0);
5549
5550 if (RT_UNLIKELY(fLockWrite))
5551 {
5552 rc2 = vdThreadFinishWrite(pDisk);
5553 AssertRC(rc2);
5554 }
5555 else if (RT_UNLIKELY(fLockRead))
5556 {
5557 rc2 = vdThreadFinishRead(pDisk);
5558 AssertRC(rc2);
5559 }
5560
5561 if (RT_FAILURE(rc))
5562 {
5563 if (pCache)
5564 {
5565 if (pCache->pszFilename)
5566 RTStrFree(pCache->pszFilename);
5567 RTMemFree(pCache);
5568 }
5569 }
5570
5571 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
5572 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
5573
5574 LogFlowFunc(("returns %Rrc\n", rc));
5575 return rc;
5576}
5577
5578/**
5579 * Merges two images (not necessarily with direct parent/child relationship).
5580 * As a side effect the source image and potentially the other images which
5581 * are also merged to the destination are deleted from both the disk and the
5582 * images in the HDD container.
5583 *
5584 * @returns VBox status code.
5585 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
5586 * @param pDisk Pointer to HDD container.
5587 * @param nImageFrom Name of the image file to merge from.
5588 * @param nImageTo Name of the image file to merge to.
5589 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
5590 */
5591VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
5592 unsigned nImageTo, PVDINTERFACE pVDIfsOperation)
5593{
5594 int rc = VINF_SUCCESS;
5595 int rc2;
5596 bool fLockWrite = false, fLockRead = false;
5597 void *pvBuf = NULL;
5598
5599 LogFlowFunc(("pDisk=%#p nImageFrom=%u nImageTo=%u pVDIfsOperation=%#p\n",
5600 pDisk, nImageFrom, nImageTo, pVDIfsOperation));
5601
5602 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
5603 VDINTERFACETYPE_PROGRESS);
5604 PVDINTERFACEPROGRESS pCbProgress = NULL;
5605 if (pIfProgress)
5606 pCbProgress = VDGetInterfaceProgress(pIfProgress);
5607
5608 do
5609 {
5610 /* sanity check */
5611 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
5612 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5613
5614 /* For simplicity reasons lock for writing as the image reopen below
5615 * might need it. After all the reopen is usually needed. */
5616 rc2 = vdThreadStartWrite(pDisk);
5617 AssertRC(rc2);
5618 fLockWrite = true;
5619 PVDIMAGE pImageFrom = vdGetImageByNumber(pDisk, nImageFrom);
5620 PVDIMAGE pImageTo = vdGetImageByNumber(pDisk, nImageTo);
5621 if (!pImageFrom || !pImageTo)
5622 {
5623 rc = VERR_VD_IMAGE_NOT_FOUND;
5624 break;
5625 }
5626 AssertBreakStmt(pImageFrom != pImageTo, rc = VERR_INVALID_PARAMETER);
5627
5628 /* Make sure destination image is writable. */
5629 unsigned uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pBackendData);
5630 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
5631 {
5632 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
5633 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
5634 uOpenFlags);
5635 if (RT_FAILURE(rc))
5636 break;
5637 }
5638
5639 /* Get size of destination image. */
5640 uint64_t cbSize = pImageTo->Backend->pfnGetSize(pImageTo->pBackendData);
5641 rc2 = vdThreadFinishWrite(pDisk);
5642 AssertRC(rc2);
5643 fLockWrite = false;
5644
5645 /* Allocate tmp buffer. */
5646 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
5647 if (!pvBuf)
5648 {
5649 rc = VERR_NO_MEMORY;
5650 break;
5651 }
5652
5653 /* Merging is done directly on the images itself. This potentially
5654 * causes trouble if the disk is full in the middle of operation. */
5655 if (nImageFrom < nImageTo)
5656 {
5657 /* Merge parent state into child. This means writing all not
5658 * allocated blocks in the destination image which are allocated in
5659 * the images to be merged. */
5660 uint64_t uOffset = 0;
5661 uint64_t cbRemaining = cbSize;
5662 do
5663 {
5664 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
5665
5666 /* Need to hold the write lock during a read-write operation. */
5667 rc2 = vdThreadStartWrite(pDisk);
5668 AssertRC(rc2);
5669 fLockWrite = true;
5670
5671 rc = pImageTo->Backend->pfnRead(pImageTo->pBackendData,
5672 uOffset, pvBuf, cbThisRead,
5673 &cbThisRead);
5674 if (rc == VERR_VD_BLOCK_FREE)
5675 {
5676 /* Search for image with allocated block. Do not attempt to
5677 * read more than the previous reads marked as valid.
5678 * Otherwise this would return stale data when different
5679 * block sizes are used for the images. */
5680 for (PVDIMAGE pCurrImage = pImageTo->pPrev;
5681 pCurrImage != NULL && pCurrImage != pImageFrom->pPrev && rc == VERR_VD_BLOCK_FREE;
5682 pCurrImage = pCurrImage->pPrev)
5683 {
5684 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
5685 uOffset, pvBuf,
5686 cbThisRead,
5687 &cbThisRead);
5688 }
5689
5690 if (rc != VERR_VD_BLOCK_FREE)
5691 {
5692 if (RT_FAILURE(rc))
5693 break;
5694 /* Updating the cache is required because this might be a live merge. */
5695 rc = vdWriteHelperEx(pDisk, pImageTo, pImageFrom->pPrev,
5696 uOffset, pvBuf, cbThisRead,
5697 true /* fUpdateCache */, 0);
5698 if (RT_FAILURE(rc))
5699 break;
5700 }
5701 else
5702 rc = VINF_SUCCESS;
5703 }
5704 else if (RT_FAILURE(rc))
5705 break;
5706
5707 rc2 = vdThreadFinishWrite(pDisk);
5708 AssertRC(rc2);
5709 fLockWrite = false;
5710
5711 uOffset += cbThisRead;
5712 cbRemaining -= cbThisRead;
5713
5714 if (pCbProgress && pCbProgress->pfnProgress)
5715 {
5716 /** @todo r=klaus: this can update the progress to the same
5717 * percentage over and over again if the image format makes
5718 * relatively small increments. */
5719 rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
5720 uOffset * 99 / cbSize);
5721 if (RT_FAILURE(rc))
5722 break;
5723 }
5724 } while (uOffset < cbSize);
5725 }
5726 else
5727 {
5728 /*
5729 * We may need to update the parent uuid of the child coming after
5730 * the last image to be merged. We have to reopen it read/write.
5731 *
5732 * This is done before we do the actual merge to prevent an
5733 * inconsistent chain if the mode change fails for some reason.
5734 */
5735 if (pImageFrom->pNext)
5736 {
5737 PVDIMAGE pImageChild = pImageFrom->pNext;
5738
5739 /* Take the write lock. */
5740 rc2 = vdThreadStartWrite(pDisk);
5741 AssertRC(rc2);
5742 fLockWrite = true;
5743
5744 /* We need to open the image in read/write mode. */
5745 uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pBackendData);
5746
5747 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
5748 {
5749 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
5750 rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pBackendData,
5751 uOpenFlags);
5752 if (RT_FAILURE(rc))
5753 break;
5754 }
5755
5756 rc2 = vdThreadFinishWrite(pDisk);
5757 AssertRC(rc2);
5758 fLockWrite = false;
5759 }
5760
5761 /* If the merge is from the last image we have to relay all writes
5762 * to the merge destination as well, so that concurrent writes
5763 * (in case of a live merge) are handled correctly. */
5764 if (!pImageFrom->pNext)
5765 {
5766 /* Take the write lock. */
5767 rc2 = vdThreadStartWrite(pDisk);
5768 AssertRC(rc2);
5769 fLockWrite = true;
5770
5771 pDisk->pImageRelay = pImageTo;
5772
5773 rc2 = vdThreadFinishWrite(pDisk);
5774 AssertRC(rc2);
5775 fLockWrite = false;
5776 }
5777
5778 /* Merge child state into parent. This means writing all blocks
5779 * which are allocated in the image up to the source image to the
5780 * destination image. */
5781 uint64_t uOffset = 0;
5782 uint64_t cbRemaining = cbSize;
5783 do
5784 {
5785 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
5786 rc = VERR_VD_BLOCK_FREE;
5787
5788 /* Need to hold the write lock during a read-write operation. */
5789 rc2 = vdThreadStartWrite(pDisk);
5790 AssertRC(rc2);
5791 fLockWrite = true;
5792
5793 /* Search for image with allocated block. Do not attempt to
5794 * read more than the previous reads marked as valid. Otherwise
5795 * this would return stale data when different block sizes are
5796 * used for the images. */
5797 for (PVDIMAGE pCurrImage = pImageFrom;
5798 pCurrImage != NULL && pCurrImage != pImageTo && rc == VERR_VD_BLOCK_FREE;
5799 pCurrImage = pCurrImage->pPrev)
5800 {
5801 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
5802 uOffset, pvBuf,
5803 cbThisRead, &cbThisRead);
5804 }
5805
5806 if (rc != VERR_VD_BLOCK_FREE)
5807 {
5808 if (RT_FAILURE(rc))
5809 break;
5810 rc = vdWriteHelper(pDisk, pImageTo, uOffset, pvBuf,
5811 cbThisRead, true /* fUpdateCache */);
5812 if (RT_FAILURE(rc))
5813 break;
5814 }
5815 else
5816 rc = VINF_SUCCESS;
5817
5818 rc2 = vdThreadFinishWrite(pDisk);
5819 AssertRC(rc2);
5820 fLockWrite = false;
5821
5822 uOffset += cbThisRead;
5823 cbRemaining -= cbThisRead;
5824
5825 if (pCbProgress && pCbProgress->pfnProgress)
5826 {
5827 /** @todo r=klaus: this can update the progress to the same
5828 * percentage over and over again if the image format makes
5829 * relatively small increments. */
5830 rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
5831 uOffset * 99 / cbSize);
5832 if (RT_FAILURE(rc))
5833 break;
5834 }
5835 } while (uOffset < cbSize);
5836
5837 /* In case we set up a "write proxy" image above we must clear
5838 * this again now to prevent stray writes. Failure or not. */
5839 if (!pImageFrom->pNext)
5840 {
5841 /* Take the write lock. */
5842 rc2 = vdThreadStartWrite(pDisk);
5843 AssertRC(rc2);
5844 fLockWrite = true;
5845
5846 pDisk->pImageRelay = NULL;
5847
5848 rc2 = vdThreadFinishWrite(pDisk);
5849 AssertRC(rc2);
5850 fLockWrite = false;
5851 }
5852 }
5853
5854 /*
5855 * Leave in case of an error to avoid corrupted data in the image chain
5856 * (includes cancelling the operation by the user).
5857 */
5858 if (RT_FAILURE(rc))
5859 break;
5860
5861 /* Need to hold the write lock while finishing the merge. */
5862 rc2 = vdThreadStartWrite(pDisk);
5863 AssertRC(rc2);
5864 fLockWrite = true;
5865
5866 /* Update parent UUID so that image chain is consistent. */
5867 RTUUID Uuid;
5868 PVDIMAGE pImageChild = NULL;
5869 if (nImageFrom < nImageTo)
5870 {
5871 if (pImageFrom->pPrev)
5872 {
5873 rc = pImageFrom->pPrev->Backend->pfnGetUuid(pImageFrom->pPrev->pBackendData,
5874 &Uuid);
5875 AssertRC(rc);
5876 }
5877 else
5878 RTUuidClear(&Uuid);
5879 rc = pImageTo->Backend->pfnSetParentUuid(pImageTo->pBackendData,
5880 &Uuid);
5881 AssertRC(rc);
5882 }
5883 else
5884 {
5885 /* Update the parent uuid of the child of the last merged image. */
5886 if (pImageFrom->pNext)
5887 {
5888 rc = pImageTo->Backend->pfnGetUuid(pImageTo->pBackendData,
5889 &Uuid);
5890 AssertRC(rc);
5891
5892 rc = pImageFrom->Backend->pfnSetParentUuid(pImageFrom->pNext->pBackendData,
5893 &Uuid);
5894 AssertRC(rc);
5895
5896 pImageChild = pImageFrom->pNext;
5897 }
5898 }
5899
5900 /* Delete the no longer needed images. */
5901 PVDIMAGE pImg = pImageFrom, pTmp;
5902 while (pImg != pImageTo)
5903 {
5904 if (nImageFrom < nImageTo)
5905 pTmp = pImg->pNext;
5906 else
5907 pTmp = pImg->pPrev;
5908 vdRemoveImageFromList(pDisk, pImg);
5909 pImg->Backend->pfnClose(pImg->pBackendData, true);
5910 RTMemFree(pImg->pszFilename);
5911 RTMemFree(pImg);
5912 pImg = pTmp;
5913 }
5914
5915 /* Make sure destination image is back to read only if necessary. */
5916 if (pImageTo != pDisk->pLast)
5917 {
5918 uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pBackendData);
5919 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
5920 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
5921 uOpenFlags);
5922 if (RT_FAILURE(rc))
5923 break;
5924 }
5925
5926 /*
5927 * Make sure the child is readonly
5928 * for the child -> parent merge direction
5929 * if necessary.
5930 */
5931 if ( nImageFrom > nImageTo
5932 && pImageChild
5933 && pImageChild != pDisk->pLast)
5934 {
5935 uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pBackendData);
5936 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
5937 rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pBackendData,
5938 uOpenFlags);
5939 if (RT_FAILURE(rc))
5940 break;
5941 }
5942 } while (0);
5943
5944 if (RT_UNLIKELY(fLockWrite))
5945 {
5946 rc2 = vdThreadFinishWrite(pDisk);
5947 AssertRC(rc2);
5948 }
5949 else if (RT_UNLIKELY(fLockRead))
5950 {
5951 rc2 = vdThreadFinishRead(pDisk);
5952 AssertRC(rc2);
5953 }
5954
5955 if (pvBuf)
5956 RTMemTmpFree(pvBuf);
5957
5958 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
5959 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
5960
5961 LogFlowFunc(("returns %Rrc\n", rc));
5962 return rc;
5963}
5964
5965/**
5966 * Copies an image from one HDD container to another - extended version.
5967 * The copy is opened in the target HDD container.
5968 * It is possible to convert between different image formats, because the
5969 * backend for the destination may be different from the source.
5970 * If both the source and destination reference the same HDD container,
5971 * then the image is moved (by copying/deleting or renaming) to the new location.
5972 * The source container is unchanged if the move operation fails, otherwise
5973 * the image at the new location is opened in the same way as the old one was.
5974 *
5975 * @note The read/write accesses across disks are not synchronized, just the
5976 * accesses to each disk. Once there is a use case which requires a defined
5977 * read/write behavior in this situation this needs to be extended.
5978 *
5979 * @return VBox status code.
5980 * @return VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
5981 * @param pDiskFrom Pointer to source HDD container.
5982 * @param nImage Image number, counts from 0. 0 is always base image of container.
5983 * @param pDiskTo Pointer to destination HDD container.
5984 * @param pszBackend Name of the image file backend to use (may be NULL to use the same as the source, case insensitive).
5985 * @param pszFilename New name of the image (may be NULL to specify that the
5986 * copy destination is the destination container, or
5987 * if pDiskFrom == pDiskTo, i.e. when moving).
5988 * @param fMoveByRename If true, attempt to perform a move by renaming (if successful the new size is ignored).
5989 * @param cbSize New image size (0 means leave unchanged).
5990 * @param nImageSameFrom todo
5991 * @param nImageSameTo todo
5992 * @param uImageFlags Flags specifying special destination image features.
5993 * @param pDstUuid New UUID of the destination image. If NULL, a new UUID is created.
5994 * This parameter is used if and only if a true copy is created.
5995 * In all rename/move cases or copy to existing image cases the modification UUIDs are copied over.
5996 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
5997 * Only used if the destination image is created.
5998 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
5999 * @param pDstVDIfsImage Pointer to the per-image VD interface list, for the
6000 * destination image.
6001 * @param pDstVDIfsOperation Pointer to the per-operation VD interface list,
6002 * for the destination operation.
6003 */
6004VBOXDDU_DECL(int) VDCopyEx(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
6005 const char *pszBackend, const char *pszFilename,
6006 bool fMoveByRename, uint64_t cbSize,
6007 unsigned nImageFromSame, unsigned nImageToSame,
6008 unsigned uImageFlags, PCRTUUID pDstUuid,
6009 unsigned uOpenFlags, PVDINTERFACE pVDIfsOperation,
6010 PVDINTERFACE pDstVDIfsImage,
6011 PVDINTERFACE pDstVDIfsOperation)
6012{
6013 int rc = VINF_SUCCESS;
6014 int rc2;
6015 bool fLockReadFrom = false, fLockWriteFrom = false, fLockWriteTo = false;
6016 PVDIMAGE pImageTo = NULL;
6017
6018 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",
6019 pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename, cbSize, uImageFlags, pDstUuid, uOpenFlags, pVDIfsOperation, pDstVDIfsImage, pDstVDIfsOperation));
6020
6021 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
6022 VDINTERFACETYPE_PROGRESS);
6023 PVDINTERFACEPROGRESS pCbProgress = NULL;
6024 if (pIfProgress)
6025 pCbProgress = VDGetInterfaceProgress(pIfProgress);
6026
6027 PVDINTERFACE pDstIfProgress = VDInterfaceGet(pDstVDIfsOperation,
6028 VDINTERFACETYPE_PROGRESS);
6029 PVDINTERFACEPROGRESS pDstCbProgress = NULL;
6030 if (pDstIfProgress)
6031 pDstCbProgress = VDGetInterfaceProgress(pDstIfProgress);
6032
6033 do {
6034 /* Check arguments. */
6035 AssertMsgBreakStmt(VALID_PTR(pDiskFrom), ("pDiskFrom=%#p\n", pDiskFrom),
6036 rc = VERR_INVALID_PARAMETER);
6037 AssertMsg(pDiskFrom->u32Signature == VBOXHDDDISK_SIGNATURE,
6038 ("u32Signature=%08x\n", pDiskFrom->u32Signature));
6039
6040 rc2 = vdThreadStartRead(pDiskFrom);
6041 AssertRC(rc2);
6042 fLockReadFrom = true;
6043 PVDIMAGE pImageFrom = vdGetImageByNumber(pDiskFrom, nImage);
6044 AssertPtrBreakStmt(pImageFrom, rc = VERR_VD_IMAGE_NOT_FOUND);
6045 AssertMsgBreakStmt(VALID_PTR(pDiskTo), ("pDiskTo=%#p\n", pDiskTo),
6046 rc = VERR_INVALID_PARAMETER);
6047 AssertMsg(pDiskTo->u32Signature == VBOXHDDDISK_SIGNATURE,
6048 ("u32Signature=%08x\n", pDiskTo->u32Signature));
6049 AssertMsgBreakStmt( (nImageFromSame < nImage || nImageFromSame == VD_IMAGE_CONTENT_UNKNOWN)
6050 && (nImageToSame < pDiskTo->cImages || nImageToSame == VD_IMAGE_CONTENT_UNKNOWN)
6051 && ( (nImageFromSame == VD_IMAGE_CONTENT_UNKNOWN && nImageToSame == VD_IMAGE_CONTENT_UNKNOWN)
6052 || (nImageFromSame != VD_IMAGE_CONTENT_UNKNOWN && nImageToSame != VD_IMAGE_CONTENT_UNKNOWN)),
6053 ("nImageFromSame=%u nImageToSame=%u\n", nImageFromSame, nImageToSame),
6054 rc = VERR_INVALID_PARAMETER);
6055
6056 /* Move the image. */
6057 if (pDiskFrom == pDiskTo)
6058 {
6059 /* Rename only works when backends are the same, are file based
6060 * and the rename method is implemented. */
6061 if ( fMoveByRename
6062 && !RTStrICmp(pszBackend, pImageFrom->Backend->pszBackendName)
6063 && pImageFrom->Backend->uBackendCaps & VD_CAP_FILE
6064 && pImageFrom->Backend->pfnRename)
6065 {
6066 rc2 = vdThreadFinishRead(pDiskFrom);
6067 AssertRC(rc2);
6068 fLockReadFrom = false;
6069
6070 rc2 = vdThreadStartWrite(pDiskFrom);
6071 AssertRC(rc2);
6072 fLockWriteFrom = true;
6073 rc = pImageFrom->Backend->pfnRename(pImageFrom->pBackendData, pszFilename ? pszFilename : pImageFrom->pszFilename);
6074 break;
6075 }
6076
6077 /** @todo Moving (including shrinking/growing) of the image is
6078 * requested, but the rename attempt failed or it wasn't possible.
6079 * Must now copy image to temp location. */
6080 AssertReleaseMsgFailed(("VDCopy: moving by copy/delete not implemented\n"));
6081 }
6082
6083 /* pszFilename is allowed to be NULL, as this indicates copy to the existing image. */
6084 AssertMsgBreakStmt(pszFilename == NULL || (VALID_PTR(pszFilename) && *pszFilename),
6085 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
6086 rc = VERR_INVALID_PARAMETER);
6087
6088 uint64_t cbSizeFrom;
6089 cbSizeFrom = pImageFrom->Backend->pfnGetSize(pImageFrom->pBackendData);
6090 if (cbSizeFrom == 0)
6091 {
6092 rc = VERR_VD_VALUE_NOT_FOUND;
6093 break;
6094 }
6095
6096 VDGEOMETRY PCHSGeometryFrom = {0, 0, 0};
6097 VDGEOMETRY LCHSGeometryFrom = {0, 0, 0};
6098 pImageFrom->Backend->pfnGetPCHSGeometry(pImageFrom->pBackendData, &PCHSGeometryFrom);
6099 pImageFrom->Backend->pfnGetLCHSGeometry(pImageFrom->pBackendData, &LCHSGeometryFrom);
6100
6101 RTUUID ImageUuid, ImageModificationUuid;
6102 if (pDiskFrom != pDiskTo)
6103 {
6104 if (pDstUuid)
6105 ImageUuid = *pDstUuid;
6106 else
6107 RTUuidCreate(&ImageUuid);
6108 }
6109 else
6110 {
6111 rc = pImageFrom->Backend->pfnGetUuid(pImageFrom->pBackendData, &ImageUuid);
6112 if (RT_FAILURE(rc))
6113 RTUuidCreate(&ImageUuid);
6114 }
6115 rc = pImageFrom->Backend->pfnGetModificationUuid(pImageFrom->pBackendData, &ImageModificationUuid);
6116 if (RT_FAILURE(rc))
6117 RTUuidClear(&ImageModificationUuid);
6118
6119 char szComment[1024];
6120 rc = pImageFrom->Backend->pfnGetComment(pImageFrom->pBackendData, szComment, sizeof(szComment));
6121 if (RT_FAILURE(rc))
6122 szComment[0] = '\0';
6123 else
6124 szComment[sizeof(szComment) - 1] = '\0';
6125
6126 rc2 = vdThreadFinishRead(pDiskFrom);
6127 AssertRC(rc2);
6128 fLockReadFrom = false;
6129
6130 rc2 = vdThreadStartRead(pDiskTo);
6131 AssertRC(rc2);
6132 unsigned cImagesTo = pDiskTo->cImages;
6133 rc2 = vdThreadFinishRead(pDiskTo);
6134 AssertRC(rc2);
6135
6136 if (pszFilename)
6137 {
6138 if (cbSize == 0)
6139 cbSize = cbSizeFrom;
6140
6141 /* Create destination image with the properties of source image. */
6142 /** @todo replace the VDCreateDiff/VDCreateBase calls by direct
6143 * calls to the backend. Unifies the code and reduces the API
6144 * dependencies. Would also make the synchronization explicit. */
6145 if (cImagesTo > 0)
6146 {
6147 rc = VDCreateDiff(pDiskTo, pszBackend, pszFilename,
6148 uImageFlags, szComment, &ImageUuid,
6149 NULL /* pParentUuid */,
6150 uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
6151 pDstVDIfsImage, NULL);
6152
6153 rc2 = vdThreadStartWrite(pDiskTo);
6154 AssertRC(rc2);
6155 fLockWriteTo = true;
6156 } else {
6157 /** @todo hack to force creation of a fixed image for
6158 * the RAW backend, which can't handle anything else. */
6159 if (!RTStrICmp(pszBackend, "RAW"))
6160 uImageFlags |= VD_IMAGE_FLAGS_FIXED;
6161
6162 vdFixupPCHSGeometry(&PCHSGeometryFrom, cbSize);
6163 vdFixupLCHSGeometry(&LCHSGeometryFrom, cbSize);
6164
6165 rc = VDCreateBase(pDiskTo, pszBackend, pszFilename, cbSize,
6166 uImageFlags, szComment,
6167 &PCHSGeometryFrom, &LCHSGeometryFrom,
6168 NULL, uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
6169 pDstVDIfsImage, NULL);
6170
6171 rc2 = vdThreadStartWrite(pDiskTo);
6172 AssertRC(rc2);
6173 fLockWriteTo = true;
6174
6175 if (RT_SUCCESS(rc) && !RTUuidIsNull(&ImageUuid))
6176 pDiskTo->pLast->Backend->pfnSetUuid(pDiskTo->pLast->pBackendData, &ImageUuid);
6177 }
6178 if (RT_FAILURE(rc))
6179 break;
6180
6181 pImageTo = pDiskTo->pLast;
6182 AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
6183
6184 cbSize = RT_MIN(cbSize, cbSizeFrom);
6185 }
6186 else
6187 {
6188 pImageTo = pDiskTo->pLast;
6189 AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
6190
6191 uint64_t cbSizeTo;
6192 cbSizeTo = pImageTo->Backend->pfnGetSize(pImageTo->pBackendData);
6193 if (cbSizeTo == 0)
6194 {
6195 rc = VERR_VD_VALUE_NOT_FOUND;
6196 break;
6197 }
6198
6199 if (cbSize == 0)
6200 cbSize = RT_MIN(cbSizeFrom, cbSizeTo);
6201
6202 vdFixupPCHSGeometry(&PCHSGeometryFrom, cbSize);
6203 vdFixupLCHSGeometry(&LCHSGeometryFrom, cbSize);
6204
6205 /* Update the geometry in the destination image. */
6206 pImageTo->Backend->pfnSetPCHSGeometry(pImageTo->pBackendData, &PCHSGeometryFrom);
6207 pImageTo->Backend->pfnSetLCHSGeometry(pImageTo->pBackendData, &LCHSGeometryFrom);
6208 }
6209
6210 rc2 = vdThreadFinishWrite(pDiskTo);
6211 AssertRC(rc2);
6212 fLockWriteTo = false;
6213
6214 /* Whether we can take the optimized copy path (false) or not.
6215 * Don't optimize if the image existed or if it is a child image. */
6216 bool fSuppressRedundantIo = ( !(pszFilename == NULL || cImagesTo > 0)
6217 || (nImageToSame != UINT32_MAX));
6218
6219 /* Copy the data. */
6220 rc = vdCopyHelper(pDiskFrom, pImageFrom, pDiskTo, cbSize,
6221 nImageFromSame == VD_IMAGE_CONTENT_UNKNOWN ? 0 : nImage - nImageFromSame,
6222 nImageToSame == VD_IMAGE_CONTENT_UNKNOWN ? 0 : pDiskTo->cImages - nImageToSame + 1,
6223 fSuppressRedundantIo, pIfProgress, pCbProgress,
6224 pDstIfProgress, pDstCbProgress);
6225
6226 if (RT_SUCCESS(rc))
6227 {
6228 rc2 = vdThreadStartWrite(pDiskTo);
6229 AssertRC(rc2);
6230 fLockWriteTo = true;
6231
6232 /* Only set modification UUID if it is non-null, since the source
6233 * backend might not provide a valid modification UUID. */
6234 if (!RTUuidIsNull(&ImageModificationUuid))
6235 pImageTo->Backend->pfnSetModificationUuid(pImageTo->pBackendData, &ImageModificationUuid);
6236
6237 /* Set the requested open flags if they differ from the value
6238 * required for creating the image and copying the contents. */
6239 if ( pImageTo && pszFilename
6240 && uOpenFlags != (uOpenFlags & ~VD_OPEN_FLAGS_READONLY))
6241 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
6242 uOpenFlags);
6243 }
6244 } while (0);
6245
6246 if (RT_FAILURE(rc) && pImageTo && pszFilename)
6247 {
6248 /* Take the write lock only if it is not taken. Not worth making the
6249 * above code even more complicated. */
6250 if (RT_UNLIKELY(!fLockWriteTo))
6251 {
6252 rc2 = vdThreadStartWrite(pDiskTo);
6253 AssertRC(rc2);
6254 fLockWriteTo = true;
6255 }
6256 /* Error detected, but new image created. Remove image from list. */
6257 vdRemoveImageFromList(pDiskTo, pImageTo);
6258
6259 /* Close and delete image. */
6260 rc2 = pImageTo->Backend->pfnClose(pImageTo->pBackendData, true);
6261 AssertRC(rc2);
6262 pImageTo->pBackendData = NULL;
6263
6264 /* Free remaining resources. */
6265 if (pImageTo->pszFilename)
6266 RTStrFree(pImageTo->pszFilename);
6267
6268 RTMemFree(pImageTo);
6269 }
6270
6271 if (RT_UNLIKELY(fLockWriteTo))
6272 {
6273 rc2 = vdThreadFinishWrite(pDiskTo);
6274 AssertRC(rc2);
6275 }
6276 if (RT_UNLIKELY(fLockWriteFrom))
6277 {
6278 rc2 = vdThreadFinishWrite(pDiskFrom);
6279 AssertRC(rc2);
6280 }
6281 else if (RT_UNLIKELY(fLockReadFrom))
6282 {
6283 rc2 = vdThreadFinishRead(pDiskFrom);
6284 AssertRC(rc2);
6285 }
6286
6287 if (RT_SUCCESS(rc))
6288 {
6289 if (pCbProgress && pCbProgress->pfnProgress)
6290 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
6291 if (pDstCbProgress && pDstCbProgress->pfnProgress)
6292 pDstCbProgress->pfnProgress(pDstIfProgress->pvUser, 100);
6293 }
6294
6295 LogFlowFunc(("returns %Rrc\n", rc));
6296 return rc;
6297}
6298
6299/**
6300 * Copies an image from one HDD container to another.
6301 * The copy is opened in the target HDD container.
6302 * It is possible to convert between different image formats, because the
6303 * backend for the destination may be different from the source.
6304 * If both the source and destination reference the same HDD container,
6305 * then the image is moved (by copying/deleting or renaming) to the new location.
6306 * The source container is unchanged if the move operation fails, otherwise
6307 * the image at the new location is opened in the same way as the old one was.
6308 *
6309 * @returns VBox status code.
6310 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
6311 * @param pDiskFrom Pointer to source HDD container.
6312 * @param nImage Image number, counts from 0. 0 is always base image of container.
6313 * @param pDiskTo Pointer to destination HDD container.
6314 * @param pszBackend Name of the image file backend to use.
6315 * @param pszFilename New name of the image (may be NULL if pDiskFrom == pDiskTo).
6316 * @param fMoveByRename If true, attempt to perform a move by renaming (if successful the new size is ignored).
6317 * @param cbSize New image size (0 means leave unchanged).
6318 * @param uImageFlags Flags specifying special destination image features.
6319 * @param pDstUuid New UUID of the destination image. If NULL, a new UUID is created.
6320 * This parameter is used if and only if a true copy is created.
6321 * In all rename/move cases the UUIDs are copied over.
6322 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
6323 * Only used if the destination image is created.
6324 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
6325 * @param pDstVDIfsImage Pointer to the per-image VD interface list, for the
6326 * destination image.
6327 * @param pDstVDIfsOperation Pointer to the per-image VD interface list,
6328 * for the destination image.
6329 */
6330VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
6331 const char *pszBackend, const char *pszFilename,
6332 bool fMoveByRename, uint64_t cbSize,
6333 unsigned uImageFlags, PCRTUUID pDstUuid,
6334 unsigned uOpenFlags, PVDINTERFACE pVDIfsOperation,
6335 PVDINTERFACE pDstVDIfsImage,
6336 PVDINTERFACE pDstVDIfsOperation)
6337{
6338 return VDCopyEx(pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename,
6339 cbSize, VD_IMAGE_CONTENT_UNKNOWN, VD_IMAGE_CONTENT_UNKNOWN,
6340 uImageFlags, pDstUuid, uOpenFlags, pVDIfsOperation,
6341 pDstVDIfsImage, pDstVDIfsOperation);
6342}
6343
6344/**
6345 * Optimizes the storage consumption of an image. Typically the unused blocks
6346 * have to be wiped with zeroes to achieve a substantial reduced storage use.
6347 * Another optimization done is reordering the image blocks, which can provide
6348 * a significant performance boost, as reads and writes tend to use less random
6349 * file offsets.
6350 *
6351 * @return VBox status code.
6352 * @return VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
6353 * @return VERR_VD_IMAGE_READ_ONLY if image is not writable.
6354 * @return VERR_NOT_SUPPORTED if this kind of image can be compacted, but
6355 * the code for this isn't implemented yet.
6356 * @param pDisk Pointer to HDD container.
6357 * @param nImage Image number, counts from 0. 0 is always base image of container.
6358 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
6359 */
6360VBOXDDU_DECL(int) VDCompact(PVBOXHDD pDisk, unsigned nImage,
6361 PVDINTERFACE pVDIfsOperation)
6362{
6363 int rc = VINF_SUCCESS;
6364 int rc2;
6365 bool fLockRead = false, fLockWrite = false;
6366 void *pvBuf = NULL;
6367 void *pvTmp = NULL;
6368
6369 LogFlowFunc(("pDisk=%#p nImage=%u pVDIfsOperation=%#p\n",
6370 pDisk, nImage, pVDIfsOperation));
6371
6372 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
6373 VDINTERFACETYPE_PROGRESS);
6374 PVDINTERFACEPROGRESS pCbProgress = NULL;
6375 if (pIfProgress)
6376 pCbProgress = VDGetInterfaceProgress(pIfProgress);
6377
6378 do {
6379 /* Check arguments. */
6380 AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
6381 rc = VERR_INVALID_PARAMETER);
6382 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE,
6383 ("u32Signature=%08x\n", pDisk->u32Signature));
6384
6385 rc2 = vdThreadStartRead(pDisk);
6386 AssertRC(rc2);
6387 fLockRead = true;
6388
6389 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6390 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
6391
6392 /* If there is no compact callback for not file based backends then
6393 * the backend doesn't need compaction. No need to make much fuss about
6394 * this. For file based ones signal this as not yet supported. */
6395 if (!pImage->Backend->pfnCompact)
6396 {
6397 if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
6398 rc = VERR_NOT_SUPPORTED;
6399 else
6400 rc = VINF_SUCCESS;
6401 break;
6402 }
6403
6404 /* Insert interface for reading parent state into per-operation list,
6405 * if there is a parent image. */
6406 VDINTERFACE IfOpParent;
6407 VDINTERFACEPARENTSTATE ParentCb;
6408 VDPARENTSTATEDESC ParentUser;
6409 if (pImage->pPrev)
6410 {
6411 ParentCb.cbSize = sizeof(ParentCb);
6412 ParentCb.enmInterface = VDINTERFACETYPE_PARENTSTATE;
6413 ParentCb.pfnParentRead = vdParentRead;
6414 ParentUser.pDisk = pDisk;
6415 ParentUser.pImage = pImage->pPrev;
6416 rc = VDInterfaceAdd(&IfOpParent, "VDCompact_ParentState", VDINTERFACETYPE_PARENTSTATE,
6417 &ParentCb, &ParentUser, &pVDIfsOperation);
6418 AssertRC(rc);
6419 }
6420
6421 rc2 = vdThreadFinishRead(pDisk);
6422 AssertRC(rc2);
6423 fLockRead = false;
6424
6425 rc2 = vdThreadStartWrite(pDisk);
6426 AssertRC(rc2);
6427 fLockWrite = true;
6428
6429 rc = pImage->Backend->pfnCompact(pImage->pBackendData,
6430 0, 99,
6431 pDisk->pVDIfsDisk,
6432 pImage->pVDIfsImage,
6433 pVDIfsOperation);
6434 } while (0);
6435
6436 if (RT_UNLIKELY(fLockWrite))
6437 {
6438 rc2 = vdThreadFinishWrite(pDisk);
6439 AssertRC(rc2);
6440 }
6441 else if (RT_UNLIKELY(fLockRead))
6442 {
6443 rc2 = vdThreadFinishRead(pDisk);
6444 AssertRC(rc2);
6445 }
6446
6447 if (pvBuf)
6448 RTMemTmpFree(pvBuf);
6449 if (pvTmp)
6450 RTMemTmpFree(pvTmp);
6451
6452 if (RT_SUCCESS(rc))
6453 {
6454 if (pCbProgress && pCbProgress->pfnProgress)
6455 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
6456 }
6457
6458 LogFlowFunc(("returns %Rrc\n", rc));
6459 return rc;
6460}
6461
6462/**
6463 * Resizes the the given disk image to the given size.
6464 *
6465 * @return VBox status
6466 * @return VERR_VD_IMAGE_READ_ONLY if image is not writable.
6467 * @return VERR_NOT_SUPPORTED if this kind of image can be compacted, but
6468 *
6469 * @param pDisk Pointer to the HDD container.
6470 * @param cbSize New size of the image.
6471 * @param pPCHSGeometry Pointer to the new physical disk geometry <= (16383,16,63). Not NULL.
6472 * @param pLCHSGeometry Pointer to the new logical disk geometry <= (x,255,63). Not NULL.
6473 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
6474 */
6475VBOXDDU_DECL(int) VDResize(PVBOXHDD pDisk, uint64_t cbSize,
6476 PCVDGEOMETRY pPCHSGeometry,
6477 PCVDGEOMETRY pLCHSGeometry,
6478 PVDINTERFACE pVDIfsOperation)
6479{
6480 /** @todo r=klaus resizing was designed to be part of VDCopy, so having a separate function is not desirable. */
6481 int rc = VINF_SUCCESS;
6482 int rc2;
6483 bool fLockRead = false, fLockWrite = false;
6484
6485 LogFlowFunc(("pDisk=%#p cbSize=%llu pVDIfsOperation=%#p\n",
6486 pDisk, cbSize, pVDIfsOperation));
6487
6488 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
6489 VDINTERFACETYPE_PROGRESS);
6490 PVDINTERFACEPROGRESS pCbProgress = NULL;
6491 if (pIfProgress)
6492 pCbProgress = VDGetInterfaceProgress(pIfProgress);
6493
6494 do {
6495 /* Check arguments. */
6496 AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
6497 rc = VERR_INVALID_PARAMETER);
6498 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE,
6499 ("u32Signature=%08x\n", pDisk->u32Signature));
6500
6501 rc2 = vdThreadStartRead(pDisk);
6502 AssertRC(rc2);
6503 fLockRead = true;
6504
6505 /* Not supported if the disk has child images attached. */
6506 AssertMsgBreakStmt(pDisk->cImages == 1, ("cImages=%u\n", pDisk->cImages),
6507 rc = VERR_NOT_SUPPORTED);
6508
6509 PVDIMAGE pImage = pDisk->pBase;
6510
6511 /* If there is no compact callback for not file based backends then
6512 * the backend doesn't need compaction. No need to make much fuss about
6513 * this. For file based ones signal this as not yet supported. */
6514 if (!pImage->Backend->pfnResize)
6515 {
6516 if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
6517 rc = VERR_NOT_SUPPORTED;
6518 else
6519 rc = VINF_SUCCESS;
6520 break;
6521 }
6522
6523 rc2 = vdThreadFinishRead(pDisk);
6524 AssertRC(rc2);
6525 fLockRead = false;
6526
6527 rc2 = vdThreadStartWrite(pDisk);
6528 AssertRC(rc2);
6529 fLockWrite = true;
6530
6531 VDGEOMETRY PCHSGeometryOld;
6532 VDGEOMETRY LCHSGeometryOld;
6533 PCVDGEOMETRY pPCHSGeometryNew;
6534 PCVDGEOMETRY pLCHSGeometryNew;
6535
6536 if (pPCHSGeometry->cCylinders == 0)
6537 {
6538 /* Auto-detect marker, calculate new value ourself. */
6539 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData, &PCHSGeometryOld);
6540 if (RT_SUCCESS(rc) && (PCHSGeometryOld.cCylinders != 0))
6541 PCHSGeometryOld.cCylinders = RT_MIN(cbSize / 512 / PCHSGeometryOld.cHeads / PCHSGeometryOld.cSectors, 16383);
6542 else if (rc == VERR_VD_GEOMETRY_NOT_SET)
6543 rc = VINF_SUCCESS;
6544
6545 pPCHSGeometryNew = &PCHSGeometryOld;
6546 }
6547 else
6548 pPCHSGeometryNew = pPCHSGeometry;
6549
6550 if (pLCHSGeometry->cCylinders == 0)
6551 {
6552 /* Auto-detect marker, calculate new value ourself. */
6553 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData, &LCHSGeometryOld);
6554 if (RT_SUCCESS(rc) && (LCHSGeometryOld.cCylinders != 0))
6555 LCHSGeometryOld.cCylinders = cbSize / 512 / LCHSGeometryOld.cHeads / LCHSGeometryOld.cSectors;
6556 else if (rc == VERR_VD_GEOMETRY_NOT_SET)
6557 rc = VINF_SUCCESS;
6558
6559 pLCHSGeometryNew = &LCHSGeometryOld;
6560 }
6561 else
6562 pLCHSGeometryNew = pLCHSGeometry;
6563
6564 if (RT_SUCCESS(rc))
6565 rc = pImage->Backend->pfnResize(pImage->pBackendData,
6566 cbSize,
6567 pPCHSGeometryNew,
6568 pLCHSGeometryNew,
6569 0, 99,
6570 pDisk->pVDIfsDisk,
6571 pImage->pVDIfsImage,
6572 pVDIfsOperation);
6573 } while (0);
6574
6575 if (RT_UNLIKELY(fLockWrite))
6576 {
6577 rc2 = vdThreadFinishWrite(pDisk);
6578 AssertRC(rc2);
6579 }
6580 else if (RT_UNLIKELY(fLockRead))
6581 {
6582 rc2 = vdThreadFinishRead(pDisk);
6583 AssertRC(rc2);
6584 }
6585
6586 if (RT_SUCCESS(rc))
6587 {
6588 if (pCbProgress && pCbProgress->pfnProgress)
6589 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
6590 }
6591
6592 LogFlowFunc(("returns %Rrc\n", rc));
6593 return rc;
6594}
6595
6596/**
6597 * Closes the last opened image file in HDD container.
6598 * If previous image file was opened in read-only mode (the normal case) and
6599 * the last opened image is in read-write mode then the previous image will be
6600 * reopened in read/write mode.
6601 *
6602 * @returns VBox status code.
6603 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
6604 * @param pDisk Pointer to HDD container.
6605 * @param fDelete If true, delete the image from the host disk.
6606 */
6607VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete)
6608{
6609 int rc = VINF_SUCCESS;
6610 int rc2;
6611 bool fLockWrite = false;
6612
6613 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
6614 do
6615 {
6616 /* sanity check */
6617 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6618 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6619
6620 /* Not worth splitting this up into a read lock phase and write
6621 * lock phase, as closing an image is a relatively fast operation
6622 * dominated by the part which needs the write lock. */
6623 rc2 = vdThreadStartWrite(pDisk);
6624 AssertRC(rc2);
6625 fLockWrite = true;
6626
6627 PVDIMAGE pImage = pDisk->pLast;
6628 if (!pImage)
6629 {
6630 rc = VERR_VD_NOT_OPENED;
6631 break;
6632 }
6633 unsigned uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
6634 /* Remove image from list of opened images. */
6635 vdRemoveImageFromList(pDisk, pImage);
6636 /* Close (and optionally delete) image. */
6637 rc = pImage->Backend->pfnClose(pImage->pBackendData, fDelete);
6638 /* Free remaining resources related to the image. */
6639 RTStrFree(pImage->pszFilename);
6640 RTMemFree(pImage);
6641
6642 pImage = pDisk->pLast;
6643 if (!pImage)
6644 break;
6645
6646 /* If disk was previously in read/write mode, make sure it will stay
6647 * like this (if possible) after closing this image. Set the open flags
6648 * accordingly. */
6649 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
6650 {
6651 uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
6652 uOpenFlags &= ~ VD_OPEN_FLAGS_READONLY;
6653 rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData, uOpenFlags);
6654 }
6655
6656 /* Cache disk information. */
6657 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
6658
6659 /* Cache PCHS geometry. */
6660 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
6661 &pDisk->PCHSGeometry);
6662 if (RT_FAILURE(rc2))
6663 {
6664 pDisk->PCHSGeometry.cCylinders = 0;
6665 pDisk->PCHSGeometry.cHeads = 0;
6666 pDisk->PCHSGeometry.cSectors = 0;
6667 }
6668 else
6669 {
6670 /* Make sure the PCHS geometry is properly clipped. */
6671 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
6672 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
6673 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
6674 }
6675
6676 /* Cache LCHS geometry. */
6677 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
6678 &pDisk->LCHSGeometry);
6679 if (RT_FAILURE(rc2))
6680 {
6681 pDisk->LCHSGeometry.cCylinders = 0;
6682 pDisk->LCHSGeometry.cHeads = 0;
6683 pDisk->LCHSGeometry.cSectors = 0;
6684 }
6685 else
6686 {
6687 /* Make sure the LCHS geometry is properly clipped. */
6688 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
6689 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
6690 }
6691 } while (0);
6692
6693 if (RT_UNLIKELY(fLockWrite))
6694 {
6695 rc2 = vdThreadFinishWrite(pDisk);
6696 AssertRC(rc2);
6697 }
6698
6699 LogFlowFunc(("returns %Rrc\n", rc));
6700 return rc;
6701}
6702
6703/**
6704 * Closes the currently opened cache image file in HDD container.
6705 *
6706 * @return VBox status code.
6707 * @return VERR_VD_NOT_OPENED if no cache is opened in HDD container.
6708 * @param pDisk Pointer to HDD container.
6709 * @param fDelete If true, delete the image from the host disk.
6710 */
6711VBOXDDU_DECL(int) VDCacheClose(PVBOXHDD pDisk, bool fDelete)
6712{
6713 int rc = VINF_SUCCESS;
6714 int rc2;
6715 bool fLockWrite = false;
6716 PVDCACHE pCache = NULL;
6717
6718 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
6719
6720 do
6721 {
6722 /* sanity check */
6723 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6724 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6725
6726 rc2 = vdThreadStartWrite(pDisk);
6727 AssertRC(rc2);
6728 fLockWrite = true;
6729
6730 AssertPtrBreakStmt(pDisk->pCache, rc = VERR_VD_CACHE_NOT_FOUND);
6731
6732 pCache = pDisk->pCache;
6733 pDisk->pCache = NULL;
6734
6735 pCache->Backend->pfnClose(pCache->pBackendData, fDelete);
6736 if (pCache->pszFilename)
6737 RTStrFree(pCache->pszFilename);
6738 RTMemFree(pCache);
6739 } while (0);
6740
6741 if (RT_LIKELY(fLockWrite))
6742 {
6743 rc2 = vdThreadFinishWrite(pDisk);
6744 AssertRC(rc2);
6745 }
6746
6747 LogFlowFunc(("returns %Rrc\n", rc));
6748 return rc;
6749}
6750
6751/**
6752 * Closes all opened image files in HDD container.
6753 *
6754 * @returns VBox status code.
6755 * @param pDisk Pointer to HDD container.
6756 */
6757VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk)
6758{
6759 int rc = VINF_SUCCESS;
6760 int rc2;
6761 bool fLockWrite = false;
6762
6763 LogFlowFunc(("pDisk=%#p\n", pDisk));
6764 do
6765 {
6766 /* sanity check */
6767 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6768 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6769
6770 /* Lock the entire operation. */
6771 rc2 = vdThreadStartWrite(pDisk);
6772 AssertRC(rc2);
6773 fLockWrite = true;
6774
6775 PVDCACHE pCache = pDisk->pCache;
6776 if (pCache)
6777 {
6778 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, false);
6779 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
6780 rc = rc2;
6781
6782 if (pCache->pszFilename)
6783 RTStrFree(pCache->pszFilename);
6784 RTMemFree(pCache);
6785 }
6786
6787 PVDIMAGE pImage = pDisk->pLast;
6788 while (VALID_PTR(pImage))
6789 {
6790 PVDIMAGE pPrev = pImage->pPrev;
6791 /* Remove image from list of opened images. */
6792 vdRemoveImageFromList(pDisk, pImage);
6793 /* Close image. */
6794 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, false);
6795 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
6796 rc = rc2;
6797 /* Free remaining resources related to the image. */
6798 RTStrFree(pImage->pszFilename);
6799 RTMemFree(pImage);
6800 pImage = pPrev;
6801 }
6802 Assert(!VALID_PTR(pDisk->pLast));
6803 } while (0);
6804
6805 if (RT_UNLIKELY(fLockWrite))
6806 {
6807 rc2 = vdThreadFinishWrite(pDisk);
6808 AssertRC(rc2);
6809 }
6810
6811 LogFlowFunc(("returns %Rrc\n", rc));
6812 return rc;
6813}
6814
6815/**
6816 * Read data from virtual HDD.
6817 *
6818 * @returns VBox status code.
6819 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
6820 * @param pDisk Pointer to HDD container.
6821 * @param uOffset Offset of first reading byte from start of disk.
6822 * @param pvBuf Pointer to buffer for reading data.
6823 * @param cbRead Number of bytes to read.
6824 */
6825VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuf,
6826 size_t cbRead)
6827{
6828 int rc = VINF_SUCCESS;
6829 int rc2;
6830 bool fLockRead = false;
6831
6832 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbRead=%zu\n",
6833 pDisk, uOffset, pvBuf, cbRead));
6834 do
6835 {
6836 /* sanity check */
6837 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6838 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6839
6840 /* Check arguments. */
6841 AssertMsgBreakStmt(VALID_PTR(pvBuf),
6842 ("pvBuf=%#p\n", pvBuf),
6843 rc = VERR_INVALID_PARAMETER);
6844 AssertMsgBreakStmt(cbRead,
6845 ("cbRead=%zu\n", cbRead),
6846 rc = VERR_INVALID_PARAMETER);
6847
6848 rc2 = vdThreadStartRead(pDisk);
6849 AssertRC(rc2);
6850 fLockRead = true;
6851
6852 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
6853 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
6854 uOffset, cbRead, pDisk->cbSize),
6855 rc = VERR_INVALID_PARAMETER);
6856
6857 PVDIMAGE pImage = pDisk->pLast;
6858 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
6859
6860 rc = vdReadHelper(pDisk, pImage, uOffset, pvBuf, cbRead,
6861 true /* fUpdateCache */);
6862 } while (0);
6863
6864 if (RT_UNLIKELY(fLockRead))
6865 {
6866 rc2 = vdThreadFinishRead(pDisk);
6867 AssertRC(rc2);
6868 }
6869
6870 LogFlowFunc(("returns %Rrc\n", rc));
6871 return rc;
6872}
6873
6874/**
6875 * Write data to virtual HDD.
6876 *
6877 * @returns VBox status code.
6878 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
6879 * @param pDisk Pointer to HDD container.
6880 * @param uOffset Offset of the first byte being
6881 * written from start of disk.
6882 * @param pvBuf Pointer to buffer for writing data.
6883 * @param cbWrite Number of bytes to write.
6884 */
6885VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf,
6886 size_t cbWrite)
6887{
6888 int rc = VINF_SUCCESS;
6889 int rc2;
6890 bool fLockWrite = false;
6891
6892 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbWrite=%zu\n",
6893 pDisk, uOffset, pvBuf, cbWrite));
6894 do
6895 {
6896 /* sanity check */
6897 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6898 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6899
6900 /* Check arguments. */
6901 AssertMsgBreakStmt(VALID_PTR(pvBuf),
6902 ("pvBuf=%#p\n", pvBuf),
6903 rc = VERR_INVALID_PARAMETER);
6904 AssertMsgBreakStmt(cbWrite,
6905 ("cbWrite=%zu\n", cbWrite),
6906 rc = VERR_INVALID_PARAMETER);
6907
6908 rc2 = vdThreadStartWrite(pDisk);
6909 AssertRC(rc2);
6910 fLockWrite = true;
6911
6912 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
6913 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
6914 uOffset, cbWrite, pDisk->cbSize),
6915 rc = VERR_INVALID_PARAMETER);
6916
6917 PVDIMAGE pImage = pDisk->pLast;
6918 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
6919
6920 vdSetModifiedFlag(pDisk);
6921 rc = vdWriteHelper(pDisk, pImage, uOffset, pvBuf, cbWrite,
6922 true /* fUpdateCache */);
6923 if (RT_FAILURE(rc))
6924 break;
6925
6926 /* If there is a merge (in the direction towards a parent) running
6927 * concurrently then we have to also "relay" the write to this parent,
6928 * as the merge position might be already past the position where
6929 * this write is going. The "context" of the write can come from the
6930 * natural chain, since merging either already did or will take care
6931 * of the "other" content which is might be needed to fill the block
6932 * to a full allocation size. The cache doesn't need to be touched
6933 * as this write is covered by the previous one. */
6934 if (RT_UNLIKELY(pDisk->pImageRelay))
6935 rc = vdWriteHelper(pDisk, pDisk->pImageRelay, uOffset,
6936 pvBuf, cbWrite, false /* fUpdateCache */);
6937 } while (0);
6938
6939 if (RT_UNLIKELY(fLockWrite))
6940 {
6941 rc2 = vdThreadFinishWrite(pDisk);
6942 AssertRC(rc2);
6943 }
6944
6945 LogFlowFunc(("returns %Rrc\n", rc));
6946 return rc;
6947}
6948
6949/**
6950 * Make sure the on disk representation of a virtual HDD is up to date.
6951 *
6952 * @returns VBox status code.
6953 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
6954 * @param pDisk Pointer to HDD container.
6955 */
6956VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
6957{
6958 int rc = VINF_SUCCESS;
6959 int rc2;
6960 bool fLockWrite = false;
6961
6962 LogFlowFunc(("pDisk=%#p\n", pDisk));
6963 do
6964 {
6965 /* sanity check */
6966 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6967 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6968
6969 rc2 = vdThreadStartWrite(pDisk);
6970 AssertRC(rc2);
6971 fLockWrite = true;
6972
6973 PVDIMAGE pImage = pDisk->pLast;
6974 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
6975
6976 vdResetModifiedFlag(pDisk);
6977 rc = pImage->Backend->pfnFlush(pImage->pBackendData);
6978
6979 if ( RT_SUCCESS(rc)
6980 && pDisk->pCache)
6981 rc = pDisk->pCache->Backend->pfnFlush(pDisk->pCache->pBackendData);
6982 } while (0);
6983
6984 if (RT_UNLIKELY(fLockWrite))
6985 {
6986 rc2 = vdThreadFinishWrite(pDisk);
6987 AssertRC(rc2);
6988 }
6989
6990 LogFlowFunc(("returns %Rrc\n", rc));
6991 return rc;
6992}
6993
6994/**
6995 * Get number of opened images in HDD container.
6996 *
6997 * @returns Number of opened images for HDD container. 0 if no images have been opened.
6998 * @param pDisk Pointer to HDD container.
6999 */
7000VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk)
7001{
7002 unsigned cImages;
7003 int rc2;
7004 bool fLockRead = false;
7005
7006 LogFlowFunc(("pDisk=%#p\n", pDisk));
7007 do
7008 {
7009 /* sanity check */
7010 AssertPtrBreakStmt(pDisk, cImages = 0);
7011 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7012
7013 rc2 = vdThreadStartRead(pDisk);
7014 AssertRC(rc2);
7015 fLockRead = true;
7016
7017 cImages = pDisk->cImages;
7018 } while (0);
7019
7020 if (RT_UNLIKELY(fLockRead))
7021 {
7022 rc2 = vdThreadFinishRead(pDisk);
7023 AssertRC(rc2);
7024 }
7025
7026 LogFlowFunc(("returns %u\n", cImages));
7027 return cImages;
7028}
7029
7030/**
7031 * Get read/write mode of HDD container.
7032 *
7033 * @returns Virtual disk ReadOnly status.
7034 * @returns true if no image is opened in HDD container.
7035 * @param pDisk Pointer to HDD container.
7036 */
7037VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
7038{
7039 bool fReadOnly;
7040 int rc2;
7041 bool fLockRead = false;
7042
7043 LogFlowFunc(("pDisk=%#p\n", pDisk));
7044 do
7045 {
7046 /* sanity check */
7047 AssertPtrBreakStmt(pDisk, fReadOnly = false);
7048 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7049
7050 rc2 = vdThreadStartRead(pDisk);
7051 AssertRC(rc2);
7052 fLockRead = true;
7053
7054 PVDIMAGE pImage = pDisk->pLast;
7055 AssertPtrBreakStmt(pImage, fReadOnly = true);
7056
7057 unsigned uOpenFlags;
7058 uOpenFlags = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
7059 fReadOnly = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
7060 } while (0);
7061
7062 if (RT_UNLIKELY(fLockRead))
7063 {
7064 rc2 = vdThreadFinishRead(pDisk);
7065 AssertRC(rc2);
7066 }
7067
7068 LogFlowFunc(("returns %d\n", fReadOnly));
7069 return fReadOnly;
7070}
7071
7072/**
7073 * Get total capacity of an image in HDD container.
7074 *
7075 * @returns Virtual disk size in bytes.
7076 * @returns 0 if no image with specified number was not opened.
7077 * @param pDisk Pointer to HDD container.
7078 * @param nImage Image number, counts from 0. 0 is always base image of container.
7079 */
7080VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk, unsigned nImage)
7081{
7082 uint64_t cbSize;
7083 int rc2;
7084 bool fLockRead = false;
7085
7086 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
7087 do
7088 {
7089 /* sanity check */
7090 AssertPtrBreakStmt(pDisk, cbSize = 0);
7091 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7092
7093 rc2 = vdThreadStartRead(pDisk);
7094 AssertRC(rc2);
7095 fLockRead = true;
7096
7097 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7098 AssertPtrBreakStmt(pImage, cbSize = 0);
7099 cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
7100 } while (0);
7101
7102 if (RT_UNLIKELY(fLockRead))
7103 {
7104 rc2 = vdThreadFinishRead(pDisk);
7105 AssertRC(rc2);
7106 }
7107
7108 LogFlowFunc(("returns %llu\n", cbSize));
7109 return cbSize;
7110}
7111
7112/**
7113 * Get total file size of an image in HDD container.
7114 *
7115 * @returns Virtual disk size in bytes.
7116 * @returns 0 if no image is opened in HDD container.
7117 * @param pDisk Pointer to HDD container.
7118 * @param nImage Image number, counts from 0. 0 is always base image of container.
7119 */
7120VBOXDDU_DECL(uint64_t) VDGetFileSize(PVBOXHDD pDisk, unsigned nImage)
7121{
7122 uint64_t cbSize;
7123 int rc2;
7124 bool fLockRead = false;
7125
7126 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
7127 do
7128 {
7129 /* sanity check */
7130 AssertPtrBreakStmt(pDisk, cbSize = 0);
7131 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7132
7133 rc2 = vdThreadStartRead(pDisk);
7134 AssertRC(rc2);
7135 fLockRead = true;
7136
7137 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7138 AssertPtrBreakStmt(pImage, cbSize = 0);
7139 cbSize = pImage->Backend->pfnGetFileSize(pImage->pBackendData);
7140 } while (0);
7141
7142 if (RT_UNLIKELY(fLockRead))
7143 {
7144 rc2 = vdThreadFinishRead(pDisk);
7145 AssertRC(rc2);
7146 }
7147
7148 LogFlowFunc(("returns %llu\n", cbSize));
7149 return cbSize;
7150}
7151
7152/**
7153 * Get virtual disk PCHS geometry stored in HDD container.
7154 *
7155 * @returns VBox status code.
7156 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7157 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
7158 * @param pDisk Pointer to HDD container.
7159 * @param nImage Image number, counts from 0. 0 is always base image of container.
7160 * @param pPCHSGeometry Where to store PCHS geometry. Not NULL.
7161 */
7162VBOXDDU_DECL(int) VDGetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
7163 PVDGEOMETRY pPCHSGeometry)
7164{
7165 int rc = VINF_SUCCESS;
7166 int rc2;
7167 bool fLockRead = false;
7168
7169 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p\n",
7170 pDisk, nImage, pPCHSGeometry));
7171 do
7172 {
7173 /* sanity check */
7174 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7175 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7176
7177 /* Check arguments. */
7178 AssertMsgBreakStmt(VALID_PTR(pPCHSGeometry),
7179 ("pPCHSGeometry=%#p\n", pPCHSGeometry),
7180 rc = VERR_INVALID_PARAMETER);
7181
7182 rc2 = vdThreadStartRead(pDisk);
7183 AssertRC(rc2);
7184 fLockRead = true;
7185
7186 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7187 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7188
7189 if (pImage == pDisk->pLast)
7190 {
7191 /* Use cached information if possible. */
7192 if (pDisk->PCHSGeometry.cCylinders != 0)
7193 *pPCHSGeometry = pDisk->PCHSGeometry;
7194 else
7195 rc = VERR_VD_GEOMETRY_NOT_SET;
7196 }
7197 else
7198 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
7199 pPCHSGeometry);
7200 } while (0);
7201
7202 if (RT_UNLIKELY(fLockRead))
7203 {
7204 rc2 = vdThreadFinishRead(pDisk);
7205 AssertRC(rc2);
7206 }
7207
7208 LogFlowFunc(("%Rrc (PCHS=%u/%u/%u)\n", rc,
7209 pDisk->PCHSGeometry.cCylinders, pDisk->PCHSGeometry.cHeads,
7210 pDisk->PCHSGeometry.cSectors));
7211 return rc;
7212}
7213
7214/**
7215 * Store virtual disk PCHS geometry in HDD container.
7216 *
7217 * Note that in case of unrecoverable error all images in HDD container will be closed.
7218 *
7219 * @returns VBox status code.
7220 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7221 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
7222 * @param pDisk Pointer to HDD container.
7223 * @param nImage Image number, counts from 0. 0 is always base image of container.
7224 * @param pPCHSGeometry Where to load PCHS geometry from. Not NULL.
7225 */
7226VBOXDDU_DECL(int) VDSetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
7227 PCVDGEOMETRY pPCHSGeometry)
7228{
7229 int rc = VINF_SUCCESS;
7230 int rc2;
7231 bool fLockWrite = false;
7232
7233 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
7234 pDisk, nImage, pPCHSGeometry, pPCHSGeometry->cCylinders,
7235 pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
7236 do
7237 {
7238 /* sanity check */
7239 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7240 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7241
7242 /* Check arguments. */
7243 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
7244 && pPCHSGeometry->cHeads <= 16
7245 && pPCHSGeometry->cSectors <= 63,
7246 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
7247 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
7248 pPCHSGeometry->cSectors),
7249 rc = VERR_INVALID_PARAMETER);
7250
7251 rc2 = vdThreadStartWrite(pDisk);
7252 AssertRC(rc2);
7253 fLockWrite = true;
7254
7255 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7256 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7257
7258 if (pImage == pDisk->pLast)
7259 {
7260 if ( pPCHSGeometry->cCylinders != pDisk->PCHSGeometry.cCylinders
7261 || pPCHSGeometry->cHeads != pDisk->PCHSGeometry.cHeads
7262 || pPCHSGeometry->cSectors != pDisk->PCHSGeometry.cSectors)
7263 {
7264 /* Only update geometry if it is changed. Avoids similar checks
7265 * in every backend. Most of the time the new geometry is set
7266 * to the previous values, so no need to go through the hassle
7267 * of updating an image which could be opened in read-only mode
7268 * right now. */
7269 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pBackendData,
7270 pPCHSGeometry);
7271
7272 /* Cache new geometry values in any case. */
7273 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
7274 &pDisk->PCHSGeometry);
7275 if (RT_FAILURE(rc2))
7276 {
7277 pDisk->PCHSGeometry.cCylinders = 0;
7278 pDisk->PCHSGeometry.cHeads = 0;
7279 pDisk->PCHSGeometry.cSectors = 0;
7280 }
7281 else
7282 {
7283 /* Make sure the CHS geometry is properly clipped. */
7284 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 255);
7285 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
7286 }
7287 }
7288 }
7289 else
7290 {
7291 VDGEOMETRY PCHS;
7292 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
7293 &PCHS);
7294 if ( RT_FAILURE(rc)
7295 || pPCHSGeometry->cCylinders != PCHS.cCylinders
7296 || pPCHSGeometry->cHeads != PCHS.cHeads
7297 || pPCHSGeometry->cSectors != PCHS.cSectors)
7298 {
7299 /* Only update geometry if it is changed. Avoids similar checks
7300 * in every backend. Most of the time the new geometry is set
7301 * to the previous values, so no need to go through the hassle
7302 * of updating an image which could be opened in read-only mode
7303 * right now. */
7304 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pBackendData,
7305 pPCHSGeometry);
7306 }
7307 }
7308 } while (0);
7309
7310 if (RT_UNLIKELY(fLockWrite))
7311 {
7312 rc2 = vdThreadFinishWrite(pDisk);
7313 AssertRC(rc2);
7314 }
7315
7316 LogFlowFunc(("returns %Rrc\n", rc));
7317 return rc;
7318}
7319
7320/**
7321 * Get virtual disk LCHS geometry stored in HDD container.
7322 *
7323 * @returns VBox status code.
7324 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7325 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
7326 * @param pDisk Pointer to HDD container.
7327 * @param nImage Image number, counts from 0. 0 is always base image of container.
7328 * @param pLCHSGeometry Where to store LCHS geometry. Not NULL.
7329 */
7330VBOXDDU_DECL(int) VDGetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
7331 PVDGEOMETRY pLCHSGeometry)
7332{
7333 int rc = VINF_SUCCESS;
7334 int rc2;
7335 bool fLockRead = false;
7336
7337 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p\n",
7338 pDisk, nImage, pLCHSGeometry));
7339 do
7340 {
7341 /* sanity check */
7342 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7343 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7344
7345 /* Check arguments. */
7346 AssertMsgBreakStmt(VALID_PTR(pLCHSGeometry),
7347 ("pLCHSGeometry=%#p\n", pLCHSGeometry),
7348 rc = VERR_INVALID_PARAMETER);
7349
7350 rc2 = vdThreadStartRead(pDisk);
7351 AssertRC(rc2);
7352 fLockRead = true;
7353
7354 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7355 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7356
7357 if (pImage == pDisk->pLast)
7358 {
7359 /* Use cached information if possible. */
7360 if (pDisk->LCHSGeometry.cCylinders != 0)
7361 *pLCHSGeometry = pDisk->LCHSGeometry;
7362 else
7363 rc = VERR_VD_GEOMETRY_NOT_SET;
7364 }
7365 else
7366 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
7367 pLCHSGeometry);
7368 } while (0);
7369
7370 if (RT_UNLIKELY(fLockRead))
7371 {
7372 rc2 = vdThreadFinishRead(pDisk);
7373 AssertRC(rc2);
7374 }
7375
7376 LogFlowFunc((": %Rrc (LCHS=%u/%u/%u)\n", rc,
7377 pDisk->LCHSGeometry.cCylinders, pDisk->LCHSGeometry.cHeads,
7378 pDisk->LCHSGeometry.cSectors));
7379 return rc;
7380}
7381
7382/**
7383 * Store virtual disk LCHS geometry in HDD container.
7384 *
7385 * Note that in case of unrecoverable error all images in HDD container will be closed.
7386 *
7387 * @returns VBox status code.
7388 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7389 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
7390 * @param pDisk Pointer to HDD container.
7391 * @param nImage Image number, counts from 0. 0 is always base image of container.
7392 * @param pLCHSGeometry Where to load LCHS geometry from. Not NULL.
7393 */
7394VBOXDDU_DECL(int) VDSetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
7395 PCVDGEOMETRY pLCHSGeometry)
7396{
7397 int rc = VINF_SUCCESS;
7398 int rc2;
7399 bool fLockWrite = false;
7400
7401 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
7402 pDisk, nImage, pLCHSGeometry, pLCHSGeometry->cCylinders,
7403 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
7404 do
7405 {
7406 /* sanity check */
7407 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7408 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7409
7410 /* Check arguments. */
7411 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
7412 && pLCHSGeometry->cHeads <= 255
7413 && pLCHSGeometry->cSectors <= 63,
7414 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
7415 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
7416 pLCHSGeometry->cSectors),
7417 rc = VERR_INVALID_PARAMETER);
7418
7419 rc2 = vdThreadStartWrite(pDisk);
7420 AssertRC(rc2);
7421 fLockWrite = true;
7422
7423 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7424 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7425
7426 if (pImage == pDisk->pLast)
7427 {
7428 if ( pLCHSGeometry->cCylinders != pDisk->LCHSGeometry.cCylinders
7429 || pLCHSGeometry->cHeads != pDisk->LCHSGeometry.cHeads
7430 || pLCHSGeometry->cSectors != pDisk->LCHSGeometry.cSectors)
7431 {
7432 /* Only update geometry if it is changed. Avoids similar checks
7433 * in every backend. Most of the time the new geometry is set
7434 * to the previous values, so no need to go through the hassle
7435 * of updating an image which could be opened in read-only mode
7436 * right now. */
7437 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pBackendData,
7438 pLCHSGeometry);
7439
7440 /* Cache new geometry values in any case. */
7441 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
7442 &pDisk->LCHSGeometry);
7443 if (RT_FAILURE(rc2))
7444 {
7445 pDisk->LCHSGeometry.cCylinders = 0;
7446 pDisk->LCHSGeometry.cHeads = 0;
7447 pDisk->LCHSGeometry.cSectors = 0;
7448 }
7449 else
7450 {
7451 /* Make sure the CHS geometry is properly clipped. */
7452 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
7453 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
7454 }
7455 }
7456 }
7457 else
7458 {
7459 VDGEOMETRY LCHS;
7460 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
7461 &LCHS);
7462 if ( RT_FAILURE(rc)
7463 || pLCHSGeometry->cCylinders != LCHS.cCylinders
7464 || pLCHSGeometry->cHeads != LCHS.cHeads
7465 || pLCHSGeometry->cSectors != LCHS.cSectors)
7466 {
7467 /* Only update geometry if it is changed. Avoids similar checks
7468 * in every backend. Most of the time the new geometry is set
7469 * to the previous values, so no need to go through the hassle
7470 * of updating an image which could be opened in read-only mode
7471 * right now. */
7472 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pBackendData,
7473 pLCHSGeometry);
7474 }
7475 }
7476 } while (0);
7477
7478 if (RT_UNLIKELY(fLockWrite))
7479 {
7480 rc2 = vdThreadFinishWrite(pDisk);
7481 AssertRC(rc2);
7482 }
7483
7484 LogFlowFunc(("returns %Rrc\n", rc));
7485 return rc;
7486}
7487
7488/**
7489 * Get version of image in HDD container.
7490 *
7491 * @returns VBox status code.
7492 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7493 * @param pDisk Pointer to HDD container.
7494 * @param nImage Image number, counts from 0. 0 is always base image of container.
7495 * @param puVersion Where to store the image version.
7496 */
7497VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
7498 unsigned *puVersion)
7499{
7500 int rc = VINF_SUCCESS;
7501 int rc2;
7502 bool fLockRead = false;
7503
7504 LogFlowFunc(("pDisk=%#p nImage=%u puVersion=%#p\n",
7505 pDisk, nImage, puVersion));
7506 do
7507 {
7508 /* sanity check */
7509 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7510 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7511
7512 /* Check arguments. */
7513 AssertMsgBreakStmt(VALID_PTR(puVersion),
7514 ("puVersion=%#p\n", puVersion),
7515 rc = VERR_INVALID_PARAMETER);
7516
7517 rc2 = vdThreadStartRead(pDisk);
7518 AssertRC(rc2);
7519 fLockRead = true;
7520
7521 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7522 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7523
7524 *puVersion = pImage->Backend->pfnGetVersion(pImage->pBackendData);
7525 } while (0);
7526
7527 if (RT_UNLIKELY(fLockRead))
7528 {
7529 rc2 = vdThreadFinishRead(pDisk);
7530 AssertRC(rc2);
7531 }
7532
7533 LogFlowFunc(("returns %Rrc uVersion=%#x\n", rc, *puVersion));
7534 return rc;
7535}
7536
7537/**
7538 * List the capabilities of image backend in HDD container.
7539 *
7540 * @returns VBox status code.
7541 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7542 * @param pDisk Pointer to the HDD container.
7543 * @param nImage Image number, counts from 0. 0 is always base image of container.
7544 * @param pbackendInfo Where to store the backend information.
7545 */
7546VBOXDDU_DECL(int) VDBackendInfoSingle(PVBOXHDD pDisk, unsigned nImage,
7547 PVDBACKENDINFO pBackendInfo)
7548{
7549 int rc = VINF_SUCCESS;
7550 int rc2;
7551 bool fLockRead = false;
7552
7553 LogFlowFunc(("pDisk=%#p nImage=%u pBackendInfo=%#p\n",
7554 pDisk, nImage, pBackendInfo));
7555 do
7556 {
7557 /* sanity check */
7558 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7559 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7560
7561 /* Check arguments. */
7562 AssertMsgBreakStmt(VALID_PTR(pBackendInfo),
7563 ("pBackendInfo=%#p\n", pBackendInfo),
7564 rc = VERR_INVALID_PARAMETER);
7565
7566 rc2 = vdThreadStartRead(pDisk);
7567 AssertRC(rc2);
7568 fLockRead = true;
7569
7570 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7571 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7572
7573 pBackendInfo->pszBackend = pImage->Backend->pszBackendName;
7574 pBackendInfo->uBackendCaps = pImage->Backend->uBackendCaps;
7575 pBackendInfo->paFileExtensions = pImage->Backend->paFileExtensions;
7576 pBackendInfo->paConfigInfo = pImage->Backend->paConfigInfo;
7577 } while (0);
7578
7579 if (RT_UNLIKELY(fLockRead))
7580 {
7581 rc2 = vdThreadFinishRead(pDisk);
7582 AssertRC(rc2);
7583 }
7584
7585 LogFlowFunc(("returns %Rrc\n", rc));
7586 return rc;
7587}
7588
7589/**
7590 * Get flags of image in HDD container.
7591 *
7592 * @returns VBox status code.
7593 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7594 * @param pDisk Pointer to HDD container.
7595 * @param nImage Image number, counts from 0. 0 is always base image of container.
7596 * @param puImageFlags Where to store the image flags.
7597 */
7598VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage,
7599 unsigned *puImageFlags)
7600{
7601 int rc = VINF_SUCCESS;
7602 int rc2;
7603 bool fLockRead = false;
7604
7605 LogFlowFunc(("pDisk=%#p nImage=%u puImageFlags=%#p\n",
7606 pDisk, nImage, puImageFlags));
7607 do
7608 {
7609 /* sanity check */
7610 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7611 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7612
7613 /* Check arguments. */
7614 AssertMsgBreakStmt(VALID_PTR(puImageFlags),
7615 ("puImageFlags=%#p\n", puImageFlags),
7616 rc = VERR_INVALID_PARAMETER);
7617
7618 rc2 = vdThreadStartRead(pDisk);
7619 AssertRC(rc2);
7620 fLockRead = true;
7621
7622 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7623 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7624
7625 *puImageFlags = pImage->uImageFlags;
7626 } while (0);
7627
7628 if (RT_UNLIKELY(fLockRead))
7629 {
7630 rc2 = vdThreadFinishRead(pDisk);
7631 AssertRC(rc2);
7632 }
7633
7634 LogFlowFunc(("returns %Rrc uImageFlags=%#x\n", rc, *puImageFlags));
7635 return rc;
7636}
7637
7638/**
7639 * Get open flags of image in HDD container.
7640 *
7641 * @returns VBox status code.
7642 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7643 * @param pDisk Pointer to HDD container.
7644 * @param nImage Image number, counts from 0. 0 is always base image of container.
7645 * @param puOpenFlags Where to store the image open flags.
7646 */
7647VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
7648 unsigned *puOpenFlags)
7649{
7650 int rc = VINF_SUCCESS;
7651 int rc2;
7652 bool fLockRead = false;
7653
7654 LogFlowFunc(("pDisk=%#p nImage=%u puOpenFlags=%#p\n",
7655 pDisk, nImage, puOpenFlags));
7656 do
7657 {
7658 /* sanity check */
7659 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7660 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7661
7662 /* Check arguments. */
7663 AssertMsgBreakStmt(VALID_PTR(puOpenFlags),
7664 ("puOpenFlags=%#p\n", puOpenFlags),
7665 rc = VERR_INVALID_PARAMETER);
7666
7667 rc2 = vdThreadStartRead(pDisk);
7668 AssertRC(rc2);
7669 fLockRead = true;
7670
7671 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7672 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7673
7674 *puOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
7675 } while (0);
7676
7677 if (RT_UNLIKELY(fLockRead))
7678 {
7679 rc2 = vdThreadFinishRead(pDisk);
7680 AssertRC(rc2);
7681 }
7682
7683 LogFlowFunc(("returns %Rrc uOpenFlags=%#x\n", rc, *puOpenFlags));
7684 return rc;
7685}
7686
7687/**
7688 * Set open flags of image in HDD container.
7689 * This operation may cause file locking changes and/or files being reopened.
7690 * Note that in case of unrecoverable error all images in HDD container will be closed.
7691 *
7692 * @returns VBox status code.
7693 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7694 * @param pDisk Pointer to HDD container.
7695 * @param nImage Image number, counts from 0. 0 is always base image of container.
7696 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
7697 */
7698VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
7699 unsigned uOpenFlags)
7700{
7701 int rc;
7702 int rc2;
7703 bool fLockWrite = false;
7704
7705 LogFlowFunc(("pDisk=%#p uOpenFlags=%#u\n", pDisk, uOpenFlags));
7706 do
7707 {
7708 /* sanity check */
7709 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7710 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7711
7712 /* Check arguments. */
7713 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
7714 ("uOpenFlags=%#x\n", uOpenFlags),
7715 rc = VERR_INVALID_PARAMETER);
7716
7717 rc2 = vdThreadStartWrite(pDisk);
7718 AssertRC(rc2);
7719 fLockWrite = true;
7720
7721 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7722 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7723
7724 rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData,
7725 uOpenFlags);
7726 } while (0);
7727
7728 if (RT_UNLIKELY(fLockWrite))
7729 {
7730 rc2 = vdThreadFinishWrite(pDisk);
7731 AssertRC(rc2);
7732 }
7733
7734 LogFlowFunc(("returns %Rrc\n", rc));
7735 return rc;
7736}
7737
7738/**
7739 * Get base filename of image in HDD container. Some image formats use
7740 * other filenames as well, so don't use this for anything but informational
7741 * purposes.
7742 *
7743 * @returns VBox status code.
7744 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7745 * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
7746 * @param pDisk Pointer to HDD container.
7747 * @param nImage Image number, counts from 0. 0 is always base image of container.
7748 * @param pszFilename Where to store the image file name.
7749 * @param cbFilename Size of buffer pszFilename points to.
7750 */
7751VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
7752 char *pszFilename, unsigned cbFilename)
7753{
7754 int rc;
7755 int rc2;
7756 bool fLockRead = false;
7757
7758 LogFlowFunc(("pDisk=%#p nImage=%u pszFilename=%#p cbFilename=%u\n",
7759 pDisk, nImage, pszFilename, cbFilename));
7760 do
7761 {
7762 /* sanity check */
7763 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7764 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7765
7766 /* Check arguments. */
7767 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
7768 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
7769 rc = VERR_INVALID_PARAMETER);
7770 AssertMsgBreakStmt(cbFilename,
7771 ("cbFilename=%u\n", cbFilename),
7772 rc = VERR_INVALID_PARAMETER);
7773
7774 rc2 = vdThreadStartRead(pDisk);
7775 AssertRC(rc2);
7776 fLockRead = true;
7777
7778 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7779 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7780
7781 size_t cb = strlen(pImage->pszFilename);
7782 if (cb <= cbFilename)
7783 {
7784 strcpy(pszFilename, pImage->pszFilename);
7785 rc = VINF_SUCCESS;
7786 }
7787 else
7788 {
7789 strncpy(pszFilename, pImage->pszFilename, cbFilename - 1);
7790 pszFilename[cbFilename - 1] = '\0';
7791 rc = VERR_BUFFER_OVERFLOW;
7792 }
7793 } while (0);
7794
7795 if (RT_UNLIKELY(fLockRead))
7796 {
7797 rc2 = vdThreadFinishRead(pDisk);
7798 AssertRC(rc2);
7799 }
7800
7801 LogFlowFunc(("returns %Rrc, pszFilename=\"%s\"\n", rc, pszFilename));
7802 return rc;
7803}
7804
7805/**
7806 * Get the comment line of image in HDD container.
7807 *
7808 * @returns VBox status code.
7809 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7810 * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
7811 * @param pDisk Pointer to HDD container.
7812 * @param nImage Image number, counts from 0. 0 is always base image of container.
7813 * @param pszComment Where to store the comment string of image. NULL is ok.
7814 * @param cbComment The size of pszComment buffer. 0 is ok.
7815 */
7816VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
7817 char *pszComment, unsigned cbComment)
7818{
7819 int rc;
7820 int rc2;
7821 bool fLockRead = false;
7822
7823 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p cbComment=%u\n",
7824 pDisk, nImage, pszComment, cbComment));
7825 do
7826 {
7827 /* sanity check */
7828 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7829 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7830
7831 /* Check arguments. */
7832 AssertMsgBreakStmt(VALID_PTR(pszComment),
7833 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
7834 rc = VERR_INVALID_PARAMETER);
7835 AssertMsgBreakStmt(cbComment,
7836 ("cbComment=%u\n", cbComment),
7837 rc = VERR_INVALID_PARAMETER);
7838
7839 rc2 = vdThreadStartRead(pDisk);
7840 AssertRC(rc2);
7841 fLockRead = true;
7842
7843 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7844 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7845
7846 rc = pImage->Backend->pfnGetComment(pImage->pBackendData, pszComment,
7847 cbComment);
7848 } while (0);
7849
7850 if (RT_UNLIKELY(fLockRead))
7851 {
7852 rc2 = vdThreadFinishRead(pDisk);
7853 AssertRC(rc2);
7854 }
7855
7856 LogFlowFunc(("returns %Rrc, pszComment=\"%s\"\n", rc, pszComment));
7857 return rc;
7858}
7859
7860/**
7861 * Changes the comment line of image in HDD container.
7862 *
7863 * @returns VBox status code.
7864 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7865 * @param pDisk Pointer to HDD container.
7866 * @param nImage Image number, counts from 0. 0 is always base image of container.
7867 * @param pszComment New comment string (UTF-8). NULL is allowed to reset the comment.
7868 */
7869VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
7870 const char *pszComment)
7871{
7872 int rc;
7873 int rc2;
7874 bool fLockWrite = false;
7875
7876 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p \"%s\"\n",
7877 pDisk, nImage, pszComment, pszComment));
7878 do
7879 {
7880 /* sanity check */
7881 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7882 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7883
7884 /* Check arguments. */
7885 AssertMsgBreakStmt(VALID_PTR(pszComment) || pszComment == NULL,
7886 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
7887 rc = VERR_INVALID_PARAMETER);
7888
7889 rc2 = vdThreadStartWrite(pDisk);
7890 AssertRC(rc2);
7891 fLockWrite = true;
7892
7893 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7894 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7895
7896 rc = pImage->Backend->pfnSetComment(pImage->pBackendData, pszComment);
7897 } while (0);
7898
7899 if (RT_UNLIKELY(fLockWrite))
7900 {
7901 rc2 = vdThreadFinishWrite(pDisk);
7902 AssertRC(rc2);
7903 }
7904
7905 LogFlowFunc(("returns %Rrc\n", rc));
7906 return rc;
7907}
7908
7909
7910/**
7911 * Get UUID of image in HDD container.
7912 *
7913 * @returns VBox status code.
7914 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7915 * @param pDisk Pointer to HDD container.
7916 * @param nImage Image number, counts from 0. 0 is always base image of container.
7917 * @param pUuid Where to store the image creation UUID.
7918 */
7919VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
7920{
7921 int rc;
7922 int rc2;
7923 bool fLockRead = false;
7924
7925 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
7926 do
7927 {
7928 /* sanity check */
7929 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7930 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7931
7932 /* Check arguments. */
7933 AssertMsgBreakStmt(VALID_PTR(pUuid),
7934 ("pUuid=%#p\n", pUuid),
7935 rc = VERR_INVALID_PARAMETER);
7936
7937 rc2 = vdThreadStartRead(pDisk);
7938 AssertRC(rc2);
7939 fLockRead = true;
7940
7941 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7942 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7943
7944 rc = pImage->Backend->pfnGetUuid(pImage->pBackendData, pUuid);
7945 } while (0);
7946
7947 if (RT_UNLIKELY(fLockRead))
7948 {
7949 rc2 = vdThreadFinishRead(pDisk);
7950 AssertRC(rc2);
7951 }
7952
7953 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
7954 return rc;
7955}
7956
7957/**
7958 * Set the image's UUID. Should not be used by normal applications.
7959 *
7960 * @returns VBox status code.
7961 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7962 * @param pDisk Pointer to HDD container.
7963 * @param nImage Image number, counts from 0. 0 is always base image of container.
7964 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
7965 */
7966VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
7967{
7968 int rc;
7969 int rc2;
7970 bool fLockWrite = false;
7971
7972 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
7973 pDisk, nImage, pUuid, pUuid));
7974 do
7975 {
7976 /* sanity check */
7977 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7978 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7979
7980 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
7981 ("pUuid=%#p\n", pUuid),
7982 rc = VERR_INVALID_PARAMETER);
7983
7984 rc2 = vdThreadStartWrite(pDisk);
7985 AssertRC(rc2);
7986 fLockWrite = true;
7987
7988 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7989 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7990
7991 RTUUID Uuid;
7992 if (!pUuid)
7993 {
7994 RTUuidCreate(&Uuid);
7995 pUuid = &Uuid;
7996 }
7997 rc = pImage->Backend->pfnSetUuid(pImage->pBackendData, pUuid);
7998 } while (0);
7999
8000 if (RT_UNLIKELY(fLockWrite))
8001 {
8002 rc2 = vdThreadFinishWrite(pDisk);
8003 AssertRC(rc2);
8004 }
8005
8006 LogFlowFunc(("returns %Rrc\n", rc));
8007 return rc;
8008}
8009
8010/**
8011 * Get last modification UUID of image in HDD container.
8012 *
8013 * @returns VBox status code.
8014 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
8015 * @param pDisk Pointer to HDD container.
8016 * @param nImage Image number, counts from 0. 0 is always base image of container.
8017 * @param pUuid Where to store the image modification UUID.
8018 */
8019VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
8020{
8021 int rc = VINF_SUCCESS;
8022 int rc2;
8023 bool fLockRead = false;
8024
8025 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
8026 do
8027 {
8028 /* sanity check */
8029 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8030 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8031
8032 /* Check arguments. */
8033 AssertMsgBreakStmt(VALID_PTR(pUuid),
8034 ("pUuid=%#p\n", pUuid),
8035 rc = VERR_INVALID_PARAMETER);
8036
8037 rc2 = vdThreadStartRead(pDisk);
8038 AssertRC(rc2);
8039 fLockRead = true;
8040
8041 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8042 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
8043
8044 rc = pImage->Backend->pfnGetModificationUuid(pImage->pBackendData,
8045 pUuid);
8046 } while (0);
8047
8048 if (RT_UNLIKELY(fLockRead))
8049 {
8050 rc2 = vdThreadFinishRead(pDisk);
8051 AssertRC(rc2);
8052 }
8053
8054 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
8055 return rc;
8056}
8057
8058/**
8059 * Set the image's last modification UUID. Should not be used by normal applications.
8060 *
8061 * @returns VBox status code.
8062 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
8063 * @param pDisk Pointer to HDD container.
8064 * @param nImage Image number, counts from 0. 0 is always base image of container.
8065 * @param pUuid New modification UUID of the image. If NULL, a new UUID is created.
8066 */
8067VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
8068{
8069 int rc;
8070 int rc2;
8071 bool fLockWrite = false;
8072
8073 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
8074 pDisk, nImage, pUuid, pUuid));
8075 do
8076 {
8077 /* sanity check */
8078 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8079 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8080
8081 /* Check arguments. */
8082 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
8083 ("pUuid=%#p\n", pUuid),
8084 rc = VERR_INVALID_PARAMETER);
8085
8086 rc2 = vdThreadStartWrite(pDisk);
8087 AssertRC(rc2);
8088 fLockWrite = true;
8089
8090 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8091 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
8092
8093 RTUUID Uuid;
8094 if (!pUuid)
8095 {
8096 RTUuidCreate(&Uuid);
8097 pUuid = &Uuid;
8098 }
8099 rc = pImage->Backend->pfnSetModificationUuid(pImage->pBackendData,
8100 pUuid);
8101 } while (0);
8102
8103 if (RT_UNLIKELY(fLockWrite))
8104 {
8105 rc2 = vdThreadFinishWrite(pDisk);
8106 AssertRC(rc2);
8107 }
8108
8109 LogFlowFunc(("returns %Rrc\n", rc));
8110 return rc;
8111}
8112
8113/**
8114 * Get parent UUID of image in HDD container.
8115 *
8116 * @returns VBox status code.
8117 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
8118 * @param pDisk Pointer to HDD container.
8119 * @param nImage Image number, counts from 0. 0 is always base image of container.
8120 * @param pUuid Where to store the parent image UUID.
8121 */
8122VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
8123 PRTUUID pUuid)
8124{
8125 int rc = VINF_SUCCESS;
8126 int rc2;
8127 bool fLockRead = false;
8128
8129 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
8130 do
8131 {
8132 /* sanity check */
8133 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8134 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8135
8136 /* Check arguments. */
8137 AssertMsgBreakStmt(VALID_PTR(pUuid),
8138 ("pUuid=%#p\n", pUuid),
8139 rc = VERR_INVALID_PARAMETER);
8140
8141 rc2 = vdThreadStartRead(pDisk);
8142 AssertRC(rc2);
8143 fLockRead = true;
8144
8145 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8146 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
8147
8148 rc = pImage->Backend->pfnGetParentUuid(pImage->pBackendData, pUuid);
8149 } while (0);
8150
8151 if (RT_UNLIKELY(fLockRead))
8152 {
8153 rc2 = vdThreadFinishRead(pDisk);
8154 AssertRC(rc2);
8155 }
8156
8157 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
8158 return rc;
8159}
8160
8161/**
8162 * Set the image's parent UUID. Should not be used by normal applications.
8163 *
8164 * @returns VBox status code.
8165 * @param pDisk Pointer to HDD container.
8166 * @param nImage Image number, counts from 0. 0 is always base image of container.
8167 * @param pUuid New parent UUID of the image. If NULL, a new UUID is created.
8168 */
8169VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
8170 PCRTUUID pUuid)
8171{
8172 int rc;
8173 int rc2;
8174 bool fLockWrite = false;
8175
8176 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
8177 pDisk, nImage, pUuid, pUuid));
8178 do
8179 {
8180 /* sanity check */
8181 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8182 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8183
8184 /* Check arguments. */
8185 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
8186 ("pUuid=%#p\n", pUuid),
8187 rc = VERR_INVALID_PARAMETER);
8188
8189 rc2 = vdThreadStartWrite(pDisk);
8190 AssertRC(rc2);
8191 fLockWrite = true;
8192
8193 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8194 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
8195
8196 RTUUID Uuid;
8197 if (!pUuid)
8198 {
8199 RTUuidCreate(&Uuid);
8200 pUuid = &Uuid;
8201 }
8202 rc = pImage->Backend->pfnSetParentUuid(pImage->pBackendData, pUuid);
8203 } while (0);
8204
8205 if (RT_UNLIKELY(fLockWrite))
8206 {
8207 rc2 = vdThreadFinishWrite(pDisk);
8208 AssertRC(rc2);
8209 }
8210
8211 LogFlowFunc(("returns %Rrc\n", rc));
8212 return rc;
8213}
8214
8215
8216/**
8217 * Debug helper - dumps all opened images in HDD container into the log file.
8218 *
8219 * @param pDisk Pointer to HDD container.
8220 */
8221VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk)
8222{
8223 int rc2;
8224 bool fLockRead = false;
8225
8226 do
8227 {
8228 /* sanity check */
8229 AssertPtrBreak(pDisk);
8230 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8231
8232 if (!pDisk->pInterfaceErrorCallbacks || !VALID_PTR(pDisk->pInterfaceErrorCallbacks->pfnMessage))
8233 pDisk->pInterfaceErrorCallbacks->pfnMessage = vdLogMessage;
8234
8235 rc2 = vdThreadStartRead(pDisk);
8236 AssertRC(rc2);
8237 fLockRead = true;
8238
8239 vdMessageWrapper(pDisk, "--- Dumping VD Disk, Images=%u\n", pDisk->cImages);
8240 for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
8241 {
8242 vdMessageWrapper(pDisk, "Dumping VD image \"%s\" (Backend=%s)\n",
8243 pImage->pszFilename, pImage->Backend->pszBackendName);
8244 pImage->Backend->pfnDump(pImage->pBackendData);
8245 }
8246 } while (0);
8247
8248 if (RT_UNLIKELY(fLockRead))
8249 {
8250 rc2 = vdThreadFinishRead(pDisk);
8251 AssertRC(rc2);
8252 }
8253}
8254
8255
8256VBOXDDU_DECL(int) VDAsyncRead(PVBOXHDD pDisk, uint64_t uOffset, size_t cbRead,
8257 PCRTSGBUF pcSgBuf,
8258 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
8259 void *pvUser1, void *pvUser2)
8260{
8261 int rc = VERR_VD_BLOCK_FREE;
8262 int rc2;
8263 bool fLockRead = false;
8264 PVDIOCTX pIoCtx = NULL;
8265
8266 LogFlowFunc(("pDisk=%#p uOffset=%llu pcSgBuf=%#p cbRead=%zu pvUser1=%#p pvUser2=%#p\n",
8267 pDisk, uOffset, pcSgBuf, cbRead, pvUser1, pvUser2));
8268
8269 do
8270 {
8271 /* sanity check */
8272 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8273 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8274
8275 /* Check arguments. */
8276 AssertMsgBreakStmt(cbRead,
8277 ("cbRead=%zu\n", cbRead),
8278 rc = VERR_INVALID_PARAMETER);
8279 AssertMsgBreakStmt(VALID_PTR(pcSgBuf),
8280 ("pcSgBuf=%#p\n", pcSgBuf),
8281 rc = VERR_INVALID_PARAMETER);
8282
8283 rc2 = vdThreadStartRead(pDisk);
8284 AssertRC(rc2);
8285 fLockRead = true;
8286
8287 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
8288 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
8289 uOffset, cbRead, pDisk->cbSize),
8290 rc = VERR_INVALID_PARAMETER);
8291 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
8292
8293 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_READ, uOffset,
8294 cbRead, pDisk->pLast, pcSgBuf,
8295 pfnComplete, pvUser1, pvUser2,
8296 NULL, vdReadHelperAsync);
8297 if (!pIoCtx)
8298 {
8299 rc = VERR_NO_MEMORY;
8300 break;
8301 }
8302
8303 rc = vdIoCtxProcess(pIoCtx);
8304 if (rc == VINF_VD_ASYNC_IO_FINISHED)
8305 {
8306 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
8307 vdIoCtxFree(pDisk, pIoCtx);
8308 else
8309 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
8310 }
8311 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
8312 vdIoCtxFree(pDisk, pIoCtx);
8313
8314 } while (0);
8315
8316 if (RT_UNLIKELY(fLockRead) && ( rc == VINF_VD_ASYNC_IO_FINISHED
8317 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
8318 {
8319 rc2 = vdThreadFinishRead(pDisk);
8320 AssertRC(rc2);
8321 }
8322
8323 LogFlowFunc(("returns %Rrc\n", rc));
8324 return rc;
8325}
8326
8327
8328VBOXDDU_DECL(int) VDAsyncWrite(PVBOXHDD pDisk, uint64_t uOffset, size_t cbWrite,
8329 PCRTSGBUF pcSgBuf,
8330 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
8331 void *pvUser1, void *pvUser2)
8332{
8333 int rc;
8334 int rc2;
8335 bool fLockWrite = false;
8336 PVDIOCTX pIoCtx = NULL;
8337
8338 LogFlowFunc(("pDisk=%#p uOffset=%llu cSgBuf=%#p cbWrite=%zu pvUser1=%#p pvUser2=%#p\n",
8339 pDisk, uOffset, pcSgBuf, cbWrite, pvUser1, pvUser2));
8340 do
8341 {
8342 /* sanity check */
8343 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8344 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8345
8346 /* Check arguments. */
8347 AssertMsgBreakStmt(cbWrite,
8348 ("cbWrite=%zu\n", cbWrite),
8349 rc = VERR_INVALID_PARAMETER);
8350 AssertMsgBreakStmt(VALID_PTR(pcSgBuf),
8351 ("pcSgBuf=%#p\n", pcSgBuf),
8352 rc = VERR_INVALID_PARAMETER);
8353
8354 rc2 = vdThreadStartWrite(pDisk);
8355 AssertRC(rc2);
8356 fLockWrite = true;
8357
8358 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
8359 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
8360 uOffset, cbWrite, pDisk->cbSize),
8361 rc = VERR_INVALID_PARAMETER);
8362 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
8363
8364 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_WRITE, uOffset,
8365 cbWrite, pDisk->pLast, pcSgBuf,
8366 pfnComplete, pvUser1, pvUser2,
8367 NULL, vdWriteHelperAsync);
8368 if (!pIoCtx)
8369 {
8370 rc = VERR_NO_MEMORY;
8371 break;
8372 }
8373
8374 rc = vdIoCtxProcess(pIoCtx);
8375 if (rc == VINF_VD_ASYNC_IO_FINISHED)
8376 {
8377 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
8378 vdIoCtxFree(pDisk, pIoCtx);
8379 else
8380 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
8381 }
8382 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
8383 vdIoCtxFree(pDisk, pIoCtx);
8384 } while (0);
8385
8386 if (RT_UNLIKELY(fLockWrite) && ( rc == VINF_VD_ASYNC_IO_FINISHED
8387 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
8388 {
8389 rc2 = vdThreadFinishWrite(pDisk);
8390 AssertRC(rc2);
8391 }
8392
8393 LogFlowFunc(("returns %Rrc\n", rc));
8394 return rc;
8395}
8396
8397
8398VBOXDDU_DECL(int) VDAsyncFlush(PVBOXHDD pDisk, PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
8399 void *pvUser1, void *pvUser2)
8400{
8401 int rc;
8402 int rc2;
8403 bool fLockWrite = false;
8404 PVDIOCTX pIoCtx = NULL;
8405
8406 LogFlowFunc(("pDisk=%#p\n", pDisk));
8407
8408 do
8409 {
8410 /* sanity check */
8411 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8412 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8413
8414 rc2 = vdThreadStartWrite(pDisk);
8415 AssertRC(rc2);
8416 fLockWrite = true;
8417
8418 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
8419
8420 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_FLUSH, 0,
8421 0, pDisk->pLast, NULL,
8422 pfnComplete, pvUser1, pvUser2,
8423 NULL, vdFlushHelperAsync);
8424 if (!pIoCtx)
8425 {
8426 rc = VERR_NO_MEMORY;
8427 break;
8428 }
8429
8430 rc = vdIoCtxProcess(pIoCtx);
8431 if (rc == VINF_VD_ASYNC_IO_FINISHED)
8432 {
8433 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
8434 vdIoCtxFree(pDisk, pIoCtx);
8435 else
8436 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
8437 }
8438 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
8439 vdIoCtxFree(pDisk, pIoCtx);
8440 } while (0);
8441
8442 if (RT_UNLIKELY(fLockWrite) && ( rc == VINF_VD_ASYNC_IO_FINISHED
8443 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
8444 {
8445 rc2 = vdThreadFinishWrite(pDisk);
8446 AssertRC(rc2);
8447 }
8448
8449 LogFlowFunc(("returns %Rrc\n", rc));
8450 return rc;
8451}
8452
Note: See TracBrowser for help on using the repository browser.

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