VirtualBox

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

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

Storage: Add QED backend

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 304.1 KB
Line 
1/* $Id: VD.cpp 38463 2011-08-15 17:02:17Z 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;
450extern VBOXHDDBACKEND g_QedBackend;
451
452static unsigned g_cBackends = 0;
453static PVBOXHDDBACKEND *g_apBackends = NULL;
454static PVBOXHDDBACKEND aStaticBackends[] =
455{
456 &g_VmdkBackend,
457 &g_VDIBackend,
458 &g_VhdBackend,
459 &g_ParallelsBackend,
460 &g_DmgBackend,
461 &g_QedBackend,
462 &g_RawBackend,
463 &g_ISCSIBackend
464};
465
466/**
467 * Supported backends for the disk cache.
468 */
469extern VDCACHEBACKEND g_VciCacheBackend;
470
471static unsigned g_cCacheBackends = 0;
472static PVDCACHEBACKEND *g_apCacheBackends = NULL;
473static PVDCACHEBACKEND aStaticCacheBackends[] =
474{
475 &g_VciCacheBackend
476};
477
478/**
479 * internal: add several backends.
480 */
481static int vdAddBackends(PVBOXHDDBACKEND *ppBackends, unsigned cBackends)
482{
483 PVBOXHDDBACKEND *pTmp = (PVBOXHDDBACKEND*)RTMemRealloc(g_apBackends,
484 (g_cBackends + cBackends) * sizeof(PVBOXHDDBACKEND));
485 if (RT_UNLIKELY(!pTmp))
486 return VERR_NO_MEMORY;
487 g_apBackends = pTmp;
488 memcpy(&g_apBackends[g_cBackends], ppBackends, cBackends * sizeof(PVBOXHDDBACKEND));
489 g_cBackends += cBackends;
490 return VINF_SUCCESS;
491}
492
493/**
494 * internal: add single backend.
495 */
496DECLINLINE(int) vdAddBackend(PVBOXHDDBACKEND pBackend)
497{
498 return vdAddBackends(&pBackend, 1);
499}
500
501/**
502 * internal: add several cache backends.
503 */
504static int vdAddCacheBackends(PVDCACHEBACKEND *ppBackends, unsigned cBackends)
505{
506 PVDCACHEBACKEND *pTmp = (PVDCACHEBACKEND*)RTMemRealloc(g_apCacheBackends,
507 (g_cCacheBackends + cBackends) * sizeof(PVDCACHEBACKEND));
508 if (RT_UNLIKELY(!pTmp))
509 return VERR_NO_MEMORY;
510 g_apCacheBackends = pTmp;
511 memcpy(&g_apCacheBackends[g_cCacheBackends], ppBackends, cBackends * sizeof(PVDCACHEBACKEND));
512 g_cCacheBackends += cBackends;
513 return VINF_SUCCESS;
514}
515
516/**
517 * internal: add single cache backend.
518 */
519DECLINLINE(int) vdAddCacheBackend(PVDCACHEBACKEND pBackend)
520{
521 return vdAddCacheBackends(&pBackend, 1);
522}
523
524/**
525 * internal: issue error message.
526 */
527static int vdError(PVBOXHDD pDisk, int rc, RT_SRC_POS_DECL,
528 const char *pszFormat, ...)
529{
530 va_list va;
531 va_start(va, pszFormat);
532 if (pDisk->pInterfaceErrorCallbacks)
533 pDisk->pInterfaceErrorCallbacks->pfnError(pDisk->pInterfaceError->pvUser, rc, RT_SRC_POS_ARGS, pszFormat, va);
534 va_end(va);
535 return rc;
536}
537
538/**
539 * internal: thread synchronization, start read.
540 */
541DECLINLINE(int) vdThreadStartRead(PVBOXHDD pDisk)
542{
543 int rc = VINF_SUCCESS;
544 if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
545 rc = pDisk->pInterfaceThreadSyncCallbacks->pfnStartRead(pDisk->pInterfaceThreadSync->pvUser);
546 return rc;
547}
548
549/**
550 * internal: thread synchronization, finish read.
551 */
552DECLINLINE(int) vdThreadFinishRead(PVBOXHDD pDisk)
553{
554 int rc = VINF_SUCCESS;
555 if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
556 rc = pDisk->pInterfaceThreadSyncCallbacks->pfnFinishRead(pDisk->pInterfaceThreadSync->pvUser);
557 return rc;
558}
559
560/**
561 * internal: thread synchronization, start write.
562 */
563DECLINLINE(int) vdThreadStartWrite(PVBOXHDD pDisk)
564{
565 int rc = VINF_SUCCESS;
566 if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
567 rc = pDisk->pInterfaceThreadSyncCallbacks->pfnStartWrite(pDisk->pInterfaceThreadSync->pvUser);
568 return rc;
569}
570
571/**
572 * internal: thread synchronization, finish write.
573 */
574DECLINLINE(int) vdThreadFinishWrite(PVBOXHDD pDisk)
575{
576 int rc = VINF_SUCCESS;
577 if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
578 rc = pDisk->pInterfaceThreadSyncCallbacks->pfnFinishWrite(pDisk->pInterfaceThreadSync->pvUser);
579 return rc;
580}
581
582/**
583 * internal: find image format backend.
584 */
585static int vdFindBackend(const char *pszBackend, PCVBOXHDDBACKEND *ppBackend)
586{
587 int rc = VINF_SUCCESS;
588 PCVBOXHDDBACKEND pBackend = NULL;
589
590 if (!g_apBackends)
591 VDInit();
592
593 for (unsigned i = 0; i < g_cBackends; i++)
594 {
595 if (!RTStrICmp(pszBackend, g_apBackends[i]->pszBackendName))
596 {
597 pBackend = g_apBackends[i];
598 break;
599 }
600 }
601 *ppBackend = pBackend;
602 return rc;
603}
604
605/**
606 * internal: find cache format backend.
607 */
608static int vdFindCacheBackend(const char *pszBackend, PCVDCACHEBACKEND *ppBackend)
609{
610 int rc = VINF_SUCCESS;
611 PCVDCACHEBACKEND pBackend = NULL;
612
613 if (!g_apCacheBackends)
614 VDInit();
615
616 for (unsigned i = 0; i < g_cCacheBackends; i++)
617 {
618 if (!RTStrICmp(pszBackend, g_apCacheBackends[i]->pszBackendName))
619 {
620 pBackend = g_apCacheBackends[i];
621 break;
622 }
623 }
624 *ppBackend = pBackend;
625 return rc;
626}
627
628/**
629 * internal: add image structure to the end of images list.
630 */
631static void vdAddImageToList(PVBOXHDD pDisk, PVDIMAGE pImage)
632{
633 pImage->pPrev = NULL;
634 pImage->pNext = NULL;
635
636 if (pDisk->pBase)
637 {
638 Assert(pDisk->cImages > 0);
639 pImage->pPrev = pDisk->pLast;
640 pDisk->pLast->pNext = pImage;
641 pDisk->pLast = pImage;
642 }
643 else
644 {
645 Assert(pDisk->cImages == 0);
646 pDisk->pBase = pImage;
647 pDisk->pLast = pImage;
648 }
649
650 pDisk->cImages++;
651}
652
653/**
654 * internal: remove image structure from the images list.
655 */
656static void vdRemoveImageFromList(PVBOXHDD pDisk, PVDIMAGE pImage)
657{
658 Assert(pDisk->cImages > 0);
659
660 if (pImage->pPrev)
661 pImage->pPrev->pNext = pImage->pNext;
662 else
663 pDisk->pBase = pImage->pNext;
664
665 if (pImage->pNext)
666 pImage->pNext->pPrev = pImage->pPrev;
667 else
668 pDisk->pLast = pImage->pPrev;
669
670 pImage->pPrev = NULL;
671 pImage->pNext = NULL;
672
673 pDisk->cImages--;
674}
675
676/**
677 * internal: find image by index into the images list.
678 */
679static PVDIMAGE vdGetImageByNumber(PVBOXHDD pDisk, unsigned nImage)
680{
681 PVDIMAGE pImage = pDisk->pBase;
682 if (nImage == VD_LAST_IMAGE)
683 return pDisk->pLast;
684 while (pImage && nImage)
685 {
686 pImage = pImage->pNext;
687 nImage--;
688 }
689 return pImage;
690}
691
692/**
693 * Internal: Tries to read the desired range from the given cache.
694 *
695 * @returns VBox status code.
696 * @retval VERR_VD_BLOCK_FREE if the block is not in the cache.
697 * pcbRead will be set to the number of bytes not in the cache.
698 * Everything thereafter might be in the cache.
699 * @param pCache The cache to read from.
700 * @param uOffset Offset of the virtual disk to read.
701 * @param pvBuf Where to store the read data.
702 * @param cbRead How much to read.
703 * @param pcbRead Where to store the number of bytes actually read.
704 * On success this indicates the number of bytes read from the cache.
705 * If VERR_VD_BLOCK_FREE is returned this gives the number of bytes
706 * which are not in the cache.
707 * In both cases everything beyond this value
708 * might or might not be in the cache.
709 */
710static int vdCacheReadHelper(PVDCACHE pCache, uint64_t uOffset,
711 void *pvBuf, size_t cbRead, size_t *pcbRead)
712{
713 int rc = VINF_SUCCESS;
714
715 LogFlowFunc(("pCache=%#p uOffset=%llu pvBuf=%#p cbRead=%zu pcbRead=%#p\n",
716 pCache, uOffset, pvBuf, cbRead, pcbRead));
717
718 AssertPtr(pCache);
719 AssertPtr(pcbRead);
720
721 rc = pCache->Backend->pfnRead(pCache->pBackendData, uOffset, pvBuf,
722 cbRead, pcbRead);
723
724 LogFlowFunc(("returns rc=%Rrc pcbRead=%zu\n", rc, *pcbRead));
725 return rc;
726}
727
728/**
729 * Internal: Writes data for the given block into the cache.
730 *
731 * @returns VBox status code.
732 * @param pCache The cache to write to.
733 * @param uOffset Offset of the virtual disk to write to teh cache.
734 * @param pcvBuf The data to write.
735 * @param cbWrite How much to write.
736 * @param pcbWritten How much data could be written, optional.
737 */
738static int vdCacheWriteHelper(PVDCACHE pCache, uint64_t uOffset, const void *pcvBuf,
739 size_t cbWrite, size_t *pcbWritten)
740{
741 int rc = VINF_SUCCESS;
742
743 LogFlowFunc(("pCache=%#p uOffset=%llu pvBuf=%#p cbWrite=%zu pcbWritten=%#p\n",
744 pCache, uOffset, pcvBuf, cbWrite, pcbWritten));
745
746 AssertPtr(pCache);
747 AssertPtr(pcvBuf);
748 Assert(cbWrite > 0);
749
750 if (pcbWritten)
751 rc = pCache->Backend->pfnWrite(pCache->pBackendData, uOffset, pcvBuf,
752 cbWrite, pcbWritten);
753 else
754 {
755 size_t cbWritten = 0;
756
757 do
758 {
759 rc = pCache->Backend->pfnWrite(pCache->pBackendData, uOffset, pcvBuf,
760 cbWrite, &cbWritten);
761 uOffset += cbWritten;
762 pcvBuf = (char *)pcvBuf + cbWritten;
763 cbWrite -= cbWritten;
764 } while ( cbWrite
765 && RT_SUCCESS(rc));
766 }
767
768 LogFlowFunc(("returns rc=%Rrc pcbWritten=%zu\n",
769 rc, pcbWritten ? *pcbWritten : cbWrite));
770 return rc;
771}
772
773/**
774 * Internal: Reads a given amount of data from the image chain of the disk.
775 **/
776static int vdDiskReadHelper(PVBOXHDD pDisk, PVDIMAGE pImage, PVDIMAGE pImageParentOverride,
777 uint64_t uOffset, void *pvBuf, size_t cbRead, size_t *pcbThisRead)
778{
779 int rc = VINF_SUCCESS;
780 size_t cbThisRead = cbRead;
781
782 AssertPtr(pcbThisRead);
783
784 *pcbThisRead = 0;
785
786 /*
787 * Try to read from the given image.
788 * If the block is not allocated read from override chain if present.
789 */
790 rc = pImage->Backend->pfnRead(pImage->pBackendData,
791 uOffset, pvBuf, cbThisRead,
792 &cbThisRead);
793
794 if (rc == VERR_VD_BLOCK_FREE)
795 {
796 for (PVDIMAGE pCurrImage = pImageParentOverride ? pImageParentOverride : pImage->pPrev;
797 pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
798 pCurrImage = pCurrImage->pPrev)
799 {
800 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
801 uOffset, pvBuf, cbThisRead,
802 &cbThisRead);
803 }
804 }
805
806 if (RT_SUCCESS(rc) || rc == VERR_VD_BLOCK_FREE)
807 *pcbThisRead = cbThisRead;
808
809 return rc;
810}
811
812/**
813 * Extended version of vdReadHelper(), implementing certain optimizations
814 * for image cloning.
815 *
816 * @returns VBox status code.
817 * @param pDisk The disk to read from.
818 * @param pImage The image to start reading from.
819 * @param pImageParentOverride The parent image to read from
820 * if the starting image returns a free block.
821 * If NULL is passed the real parent of the image
822 * in the chain is used.
823 * @param uOffset Offset in the disk to start reading from.
824 * @param pvBuf Where to store the read data.
825 * @param cbRead How much to read.
826 * @param fZeroFreeBlocks Flag whether free blocks should be zeroed.
827 * If false and no image has data for sepcified
828 * range VERR_VD_BLOCK_FREE is returned.
829 * Note that unallocated blocks are still zeroed
830 * if at least one image has valid data for a part
831 * of the range.
832 * @param fUpdateCache Flag whether to update the attached cache if
833 * available.
834 * @param cImagesRead Number of images in the chain to read until
835 * the read is cut off. A value of 0 disables the cut off.
836 */
837static int vdReadHelperEx(PVBOXHDD pDisk, PVDIMAGE pImage, PVDIMAGE pImageParentOverride,
838 uint64_t uOffset, void *pvBuf, size_t cbRead,
839 bool fZeroFreeBlocks, bool fUpdateCache, unsigned cImagesRead)
840{
841 int rc = VINF_SUCCESS;
842 size_t cbThisRead;
843 bool fAllFree = true;
844 size_t cbBufClear = 0;
845
846 /* Loop until all read. */
847 do
848 {
849 /* Search for image with allocated block. Do not attempt to read more
850 * than the previous reads marked as valid. Otherwise this would return
851 * stale data when different block sizes are used for the images. */
852 cbThisRead = cbRead;
853
854 if ( pDisk->pCache
855 && !pImageParentOverride)
856 {
857 rc = vdCacheReadHelper(pDisk->pCache, uOffset, pvBuf,
858 cbThisRead, &cbThisRead);
859
860 if (rc == VERR_VD_BLOCK_FREE)
861 {
862 rc = vdDiskReadHelper(pDisk, pImage, NULL, uOffset, pvBuf, cbThisRead,
863 &cbThisRead);
864
865 /* If the read was successful, write the data back into the cache. */
866 if ( RT_SUCCESS(rc)
867 && fUpdateCache)
868 {
869 rc = vdCacheWriteHelper(pDisk->pCache, uOffset, pvBuf,
870 cbThisRead, NULL);
871 }
872 }
873 }
874 else
875 {
876 /** @todo can be be replaced by vdDiskReadHelper if it proves to be reliable,
877 * don't want to be responsible for data corruption...
878 */
879 /*
880 * Try to read from the given image.
881 * If the block is not allocated read from override chain if present.
882 */
883 rc = pImage->Backend->pfnRead(pImage->pBackendData,
884 uOffset, pvBuf, cbThisRead,
885 &cbThisRead);
886
887 if ( rc == VERR_VD_BLOCK_FREE
888 && cImagesRead != 1)
889 {
890 unsigned cImagesToProcess = cImagesRead;
891
892 for (PVDIMAGE pCurrImage = pImageParentOverride ? pImageParentOverride : pImage->pPrev;
893 pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
894 pCurrImage = pCurrImage->pPrev)
895 {
896 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
897 uOffset, pvBuf, cbThisRead,
898 &cbThisRead);
899 if (cImagesToProcess == 1)
900 break;
901 else if (cImagesToProcess > 0)
902 cImagesToProcess--;
903 }
904 }
905 }
906
907 /* No image in the chain contains the data for the block. */
908 if (rc == VERR_VD_BLOCK_FREE)
909 {
910 /* Fill the free space with 0 if we are told to do so
911 * or a previous read returned valid data. */
912 if (fZeroFreeBlocks || !fAllFree)
913 memset(pvBuf, '\0', cbThisRead);
914 else
915 cbBufClear += cbThisRead;
916
917 rc = VINF_SUCCESS;
918 }
919 else if (RT_SUCCESS(rc))
920 {
921 /* First not free block, fill the space before with 0. */
922 if (!fZeroFreeBlocks)
923 {
924 memset((char *)pvBuf - cbBufClear, '\0', cbBufClear);
925 cbBufClear = 0;
926 fAllFree = false;
927 }
928 }
929
930 cbRead -= cbThisRead;
931 uOffset += cbThisRead;
932 pvBuf = (char *)pvBuf + cbThisRead;
933 } while (cbRead != 0 && RT_SUCCESS(rc));
934
935 return (!fZeroFreeBlocks && fAllFree) ? VERR_VD_BLOCK_FREE : rc;
936}
937
938/**
939 * internal: read the specified amount of data in whatever blocks the backend
940 * will give us.
941 */
942static int vdReadHelper(PVBOXHDD pDisk, PVDIMAGE pImage, uint64_t uOffset,
943 void *pvBuf, size_t cbRead, bool fUpdateCache)
944{
945 return vdReadHelperEx(pDisk, pImage, NULL, uOffset, pvBuf, cbRead,
946 true /* fZeroFreeBlocks */, fUpdateCache, 0);
947}
948
949DECLINLINE(PVDIOCTX) vdIoCtxAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
950 uint64_t uOffset, size_t cbTransfer,
951 PVDIMAGE pImageStart,
952 PCRTSGBUF pcSgBuf, void *pvAllocation,
953 PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
954{
955 PVDIOCTX pIoCtx = NULL;
956
957 pIoCtx = (PVDIOCTX)RTMemCacheAlloc(pDisk->hMemCacheIoCtx);
958 if (RT_LIKELY(pIoCtx))
959 {
960 pIoCtx->pDisk = pDisk;
961 pIoCtx->enmTxDir = enmTxDir;
962 pIoCtx->cbTransferLeft = cbTransfer;
963 pIoCtx->uOffset = uOffset;
964 pIoCtx->cbTransfer = cbTransfer;
965 pIoCtx->pImageStart = pImageStart;
966 pIoCtx->pImageCur = pImageStart;
967 pIoCtx->cDataTransfersPending = 0;
968 pIoCtx->cMetaTransfersPending = 0;
969 pIoCtx->fComplete = false;
970 pIoCtx->fBlocked = false;
971 pIoCtx->pvAllocation = pvAllocation;
972 pIoCtx->pfnIoCtxTransfer = pfnIoCtxTransfer;
973 pIoCtx->pfnIoCtxTransferNext = NULL;
974 pIoCtx->rcReq = VINF_SUCCESS;
975
976 /* There is no S/G list for a flush request. */
977 if (enmTxDir != VDIOCTXTXDIR_FLUSH)
978 RTSgBufClone(&pIoCtx->SgBuf, pcSgBuf);
979 else
980 memset(&pIoCtx->SgBuf, 0, sizeof(RTSGBUF));
981 }
982
983 return pIoCtx;
984}
985
986DECLINLINE(PVDIOCTX) vdIoCtxRootAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
987 uint64_t uOffset, size_t cbTransfer,
988 PVDIMAGE pImageStart, PCRTSGBUF pcSgBuf,
989 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
990 void *pvUser1, void *pvUser2,
991 void *pvAllocation,
992 PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
993{
994 PVDIOCTX pIoCtx = vdIoCtxAlloc(pDisk, enmTxDir, uOffset, cbTransfer, pImageStart,
995 pcSgBuf, pvAllocation, pfnIoCtxTransfer);
996
997 if (RT_LIKELY(pIoCtx))
998 {
999 pIoCtx->pIoCtxParent = NULL;
1000 pIoCtx->Type.Root.pfnComplete = pfnComplete;
1001 pIoCtx->Type.Root.pvUser1 = pvUser1;
1002 pIoCtx->Type.Root.pvUser2 = pvUser2;
1003 }
1004
1005 LogFlow(("Allocated root I/O context %#p\n", pIoCtx));
1006 return pIoCtx;
1007}
1008
1009DECLINLINE(PVDIOCTX) vdIoCtxChildAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
1010 uint64_t uOffset, size_t cbTransfer,
1011 PVDIMAGE pImageStart, PCRTSGBUF pcSgBuf,
1012 PVDIOCTX pIoCtxParent, size_t cbTransferParent,
1013 size_t cbWriteParent, void *pvAllocation,
1014 PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
1015{
1016 PVDIOCTX pIoCtx = vdIoCtxAlloc(pDisk, enmTxDir, uOffset, cbTransfer, pImageStart,
1017 pcSgBuf, pvAllocation, pfnIoCtxTransfer);
1018
1019 AssertPtr(pIoCtxParent);
1020 Assert(!pIoCtxParent->pIoCtxParent);
1021
1022 if (RT_LIKELY(pIoCtx))
1023 {
1024 pIoCtx->pIoCtxParent = pIoCtxParent;
1025 pIoCtx->Type.Child.uOffsetSaved = uOffset;
1026 pIoCtx->Type.Child.cbTransferLeftSaved = cbTransfer;
1027 pIoCtx->Type.Child.cbTransferParent = cbTransferParent;
1028 pIoCtx->Type.Child.cbWriteParent = cbWriteParent;
1029 }
1030
1031 LogFlow(("Allocated child I/O context %#p\n", pIoCtx));
1032 return pIoCtx;
1033}
1034
1035DECLINLINE(PVDIOTASK) vdIoTaskUserAlloc(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnComplete, void *pvUser, PVDIOCTX pIoCtx, uint32_t cbTransfer)
1036{
1037 PVDIOTASK pIoTask = NULL;
1038
1039 pIoTask = (PVDIOTASK)RTMemCacheAlloc(pIoStorage->pVDIo->pDisk->hMemCacheIoTask);
1040 if (pIoTask)
1041 {
1042 pIoTask->pIoStorage = pIoStorage;
1043 pIoTask->pfnComplete = pfnComplete;
1044 pIoTask->pvUser = pvUser;
1045 pIoTask->fMeta = false;
1046 pIoTask->Type.User.cbTransfer = cbTransfer;
1047 pIoTask->Type.User.pIoCtx = pIoCtx;
1048 }
1049
1050 return pIoTask;
1051}
1052
1053DECLINLINE(PVDIOTASK) vdIoTaskMetaAlloc(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnComplete, void *pvUser, PVDMETAXFER pMetaXfer)
1054{
1055 PVDIOTASK pIoTask = NULL;
1056
1057 pIoTask = (PVDIOTASK)RTMemCacheAlloc(pIoStorage->pVDIo->pDisk->hMemCacheIoTask);
1058 if (pIoTask)
1059 {
1060 pIoTask->pIoStorage = pIoStorage;
1061 pIoTask->pfnComplete = pfnComplete;
1062 pIoTask->pvUser = pvUser;
1063 pIoTask->fMeta = true;
1064 pIoTask->Type.Meta.pMetaXfer = pMetaXfer;
1065 }
1066
1067 return pIoTask;
1068}
1069
1070DECLINLINE(void) vdIoCtxFree(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
1071{
1072 LogFlow(("Freeing I/O context %#p\n", pIoCtx));
1073 if (pIoCtx->pvAllocation)
1074 RTMemFree(pIoCtx->pvAllocation);
1075#ifdef DEBUG
1076 memset(pIoCtx, 0xff, sizeof(VDIOCTX));
1077#endif
1078 RTMemCacheFree(pDisk->hMemCacheIoCtx, pIoCtx);
1079}
1080
1081DECLINLINE(void) vdIoTaskFree(PVBOXHDD pDisk, PVDIOTASK pIoTask)
1082{
1083 RTMemCacheFree(pDisk->hMemCacheIoTask, pIoTask);
1084}
1085
1086DECLINLINE(void) vdIoCtxChildReset(PVDIOCTX pIoCtx)
1087{
1088 AssertPtr(pIoCtx->pIoCtxParent);
1089
1090 RTSgBufReset(&pIoCtx->SgBuf);
1091 pIoCtx->uOffset = pIoCtx->Type.Child.uOffsetSaved;
1092 pIoCtx->cbTransferLeft = pIoCtx->Type.Child.cbTransferLeftSaved;
1093}
1094
1095DECLINLINE(PVDMETAXFER) vdMetaXferAlloc(PVDIOSTORAGE pIoStorage, uint64_t uOffset, size_t cb)
1096{
1097 PVDMETAXFER pMetaXfer = (PVDMETAXFER)RTMemAlloc(RT_OFFSETOF(VDMETAXFER, abData[cb]));
1098
1099 if (RT_LIKELY(pMetaXfer))
1100 {
1101 pMetaXfer->Core.Key = uOffset;
1102 pMetaXfer->Core.KeyLast = uOffset + cb - 1;
1103 pMetaXfer->fFlags = VDMETAXFER_TXDIR_NONE;
1104 pMetaXfer->cbMeta = cb;
1105 pMetaXfer->pIoStorage = pIoStorage;
1106 pMetaXfer->cRefs = 0;
1107 RTListInit(&pMetaXfer->ListIoCtxWaiting);
1108 }
1109 return pMetaXfer;
1110}
1111
1112DECLINLINE(int) vdIoCtxDefer(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
1113{
1114 PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
1115
1116 if (!pDeferred)
1117 return VERR_NO_MEMORY;
1118
1119 LogFlowFunc(("Deferring write pIoCtx=%#p\n", pIoCtx));
1120
1121 Assert(!pIoCtx->pIoCtxParent && !pIoCtx->fBlocked);
1122
1123 RTListInit(&pDeferred->NodeDeferred);
1124 pDeferred->pIoCtx = pIoCtx;
1125 RTListAppend(&pDisk->ListWriteLocked, &pDeferred->NodeDeferred);
1126 pIoCtx->fBlocked = true;
1127 return VINF_SUCCESS;
1128}
1129
1130static size_t vdIoCtxCopy(PVDIOCTX pIoCtxDst, PVDIOCTX pIoCtxSrc, size_t cbData)
1131{
1132 return RTSgBufCopy(&pIoCtxDst->SgBuf, &pIoCtxSrc->SgBuf, cbData);
1133}
1134
1135static int vdIoCtxCmp(PVDIOCTX pIoCtx1, PVDIOCTX pIoCtx2, size_t cbData)
1136{
1137 return RTSgBufCmp(&pIoCtx1->SgBuf, &pIoCtx2->SgBuf, cbData);
1138}
1139
1140static size_t vdIoCtxCopyTo(PVDIOCTX pIoCtx, uint8_t *pbData, size_t cbData)
1141{
1142 return RTSgBufCopyToBuf(&pIoCtx->SgBuf, pbData, cbData);
1143}
1144
1145
1146static size_t vdIoCtxCopyFrom(PVDIOCTX pIoCtx, uint8_t *pbData, size_t cbData)
1147{
1148 return RTSgBufCopyFromBuf(&pIoCtx->SgBuf, pbData, cbData);
1149}
1150
1151static size_t vdIoCtxSet(PVDIOCTX pIoCtx, uint8_t ch, size_t cbData)
1152{
1153 return RTSgBufSet(&pIoCtx->SgBuf, ch, cbData);
1154}
1155
1156static int vdIoCtxProcess(PVDIOCTX pIoCtx)
1157{
1158 int rc = VINF_SUCCESS;
1159 PVBOXHDD pDisk = pIoCtx->pDisk;
1160
1161 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
1162
1163 RTCritSectEnter(&pDisk->CritSect);
1164
1165 if ( !pIoCtx->cbTransferLeft
1166 && !pIoCtx->cMetaTransfersPending
1167 && !pIoCtx->cDataTransfersPending
1168 && !pIoCtx->pfnIoCtxTransfer)
1169 {
1170 rc = VINF_VD_ASYNC_IO_FINISHED;
1171 goto out;
1172 }
1173
1174 /*
1175 * We complete the I/O context in case of an error
1176 * if there is no I/O task pending.
1177 */
1178 if ( RT_FAILURE(pIoCtx->rcReq)
1179 && !pIoCtx->cMetaTransfersPending
1180 && !pIoCtx->cDataTransfersPending)
1181 {
1182 rc = VINF_VD_ASYNC_IO_FINISHED;
1183 goto out;
1184 }
1185
1186 /* Don't change anything if there is a metadata transfer pending or we are blocked. */
1187 if ( pIoCtx->cMetaTransfersPending
1188 || pIoCtx->fBlocked)
1189 {
1190 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1191 goto out;
1192 }
1193
1194 if (pIoCtx->pfnIoCtxTransfer)
1195 {
1196 /* Call the transfer function advancing to the next while there is no error. */
1197 while ( pIoCtx->pfnIoCtxTransfer
1198 && RT_SUCCESS(rc))
1199 {
1200 LogFlowFunc(("calling transfer function %#p\n", pIoCtx->pfnIoCtxTransfer));
1201 rc = pIoCtx->pfnIoCtxTransfer(pIoCtx);
1202
1203 /* Advance to the next part of the transfer if the current one succeeded. */
1204 if (RT_SUCCESS(rc))
1205 {
1206 pIoCtx->pfnIoCtxTransfer = pIoCtx->pfnIoCtxTransferNext;
1207 pIoCtx->pfnIoCtxTransferNext = NULL;
1208 }
1209 }
1210 }
1211
1212 if ( RT_SUCCESS(rc)
1213 && !pIoCtx->cbTransferLeft
1214 && !pIoCtx->cMetaTransfersPending
1215 && !pIoCtx->cDataTransfersPending)
1216 rc = VINF_VD_ASYNC_IO_FINISHED;
1217 else if ( RT_SUCCESS(rc)
1218 || rc == VERR_VD_NOT_ENOUGH_METADATA
1219 || rc == VERR_VD_IOCTX_HALT)
1220 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1221 else if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
1222 {
1223 ASMAtomicCmpXchgS32(&pIoCtx->rcReq, rc, VINF_SUCCESS);
1224 /*
1225 * The I/O context completed if we have an error and there is no data
1226 * or meta data transfer pending.
1227 */
1228 if ( !pIoCtx->cMetaTransfersPending
1229 && !pIoCtx->cDataTransfersPending)
1230 rc = VINF_VD_ASYNC_IO_FINISHED;
1231 else
1232 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1233 }
1234
1235out:
1236 RTCritSectLeave(&pDisk->CritSect);
1237
1238 LogFlowFunc(("pIoCtx=%#p rc=%Rrc cbTransferLeft=%u cMetaTransfersPending=%u fComplete=%RTbool\n",
1239 pIoCtx, rc, pIoCtx->cbTransferLeft, pIoCtx->cMetaTransfersPending,
1240 pIoCtx->fComplete));
1241
1242 return rc;
1243}
1244
1245DECLINLINE(bool) vdIoCtxIsDiskLockOwner(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
1246{
1247 return pDisk->fLocked
1248 && pDisk->pIoCtxLockOwner == pIoCtx;
1249}
1250
1251static int vdIoCtxLockDisk(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
1252{
1253 int rc = VINF_SUCCESS;
1254
1255 LogFlowFunc(("pDisk=%#p pIoCtx=%#p\n", pDisk, pIoCtx));
1256
1257 if (!ASMAtomicCmpXchgBool(&pDisk->fLocked, true, false))
1258 {
1259 Assert(pDisk->pIoCtxLockOwner != pIoCtx); /* No nesting allowed. */
1260
1261 rc = vdIoCtxDefer(pDisk, pIoCtx);
1262 if (RT_SUCCESS(rc))
1263 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1264 }
1265 else
1266 {
1267 Assert(!pDisk->pIoCtxLockOwner);
1268 pDisk->pIoCtxLockOwner = pIoCtx;
1269 }
1270
1271 LogFlowFunc(("returns -> %Rrc\n", rc));
1272 return rc;
1273}
1274
1275static void vdIoCtxUnlockDisk(PVBOXHDD pDisk, PVDIOCTX pIoCtx, bool fProcessDeferredReqs)
1276{
1277 LogFlowFunc(("pDisk=%#p pIoCtx=%#p fProcessDeferredReqs=%RTbool\n",
1278 pDisk, pIoCtx, fProcessDeferredReqs));
1279
1280 LogFlow(("Unlocking disk lock owner is %#p\n", pDisk->pIoCtxLockOwner));
1281 Assert(pDisk->fLocked);
1282 Assert(pDisk->pIoCtxLockOwner == pIoCtx);
1283 pDisk->pIoCtxLockOwner = NULL;
1284 ASMAtomicXchgBool(&pDisk->fLocked, false);
1285
1286 if (fProcessDeferredReqs)
1287 {
1288 /* Process any pending writes if the current request didn't caused another growing. */
1289 RTCritSectEnter(&pDisk->CritSect);
1290
1291 if (!RTListIsEmpty(&pDisk->ListWriteLocked))
1292 {
1293 RTLISTNODE ListTmp;
1294
1295 RTListMove(&ListTmp, &pDisk->ListWriteLocked);
1296 RTCritSectLeave(&pDisk->CritSect);
1297
1298 /* Process the list. */
1299 do
1300 {
1301 int rc;
1302 PVDIOCTXDEFERRED pDeferred = RTListGetFirst(&ListTmp, VDIOCTXDEFERRED, NodeDeferred);
1303 PVDIOCTX pIoCtxWait = pDeferred->pIoCtx;
1304
1305 AssertPtr(pIoCtxWait);
1306
1307 RTListNodeRemove(&pDeferred->NodeDeferred);
1308 RTMemFree(pDeferred);
1309
1310 Assert(!pIoCtxWait->pIoCtxParent);
1311
1312 pIoCtxWait->fBlocked = false;
1313 LogFlowFunc(("Processing waiting I/O context pIoCtxWait=%#p\n", pIoCtxWait));
1314
1315 rc = vdIoCtxProcess(pIoCtxWait);
1316 if ( rc == VINF_VD_ASYNC_IO_FINISHED
1317 && ASMAtomicCmpXchgBool(&pIoCtxWait->fComplete, true, false))
1318 {
1319 LogFlowFunc(("Waiting I/O context completed pIoCtxWait=%#p\n", pIoCtxWait));
1320 vdThreadFinishWrite(pDisk);
1321 pIoCtxWait->Type.Root.pfnComplete(pIoCtxWait->Type.Root.pvUser1,
1322 pIoCtxWait->Type.Root.pvUser2,
1323 pIoCtxWait->rcReq);
1324 vdIoCtxFree(pDisk, pIoCtxWait);
1325 }
1326 } while (!RTListIsEmpty(&ListTmp));
1327 }
1328 else
1329 RTCritSectLeave(&pDisk->CritSect);
1330 }
1331
1332 LogFlowFunc(("returns\n"));
1333}
1334
1335/**
1336 * internal: read the specified amount of data in whatever blocks the backend
1337 * will give us - async version.
1338 */
1339static int vdReadHelperAsync(PVDIOCTX pIoCtx)
1340{
1341 int rc;
1342 size_t cbToRead = pIoCtx->cbTransfer;
1343 uint64_t uOffset = pIoCtx->uOffset;
1344 PVDIMAGE pCurrImage = NULL;
1345 size_t cbThisRead;
1346
1347 /* Loop until all reads started or we have a backend which needs to read metadata. */
1348 do
1349 {
1350 pCurrImage = pIoCtx->pImageCur;
1351
1352 /* Search for image with allocated block. Do not attempt to read more
1353 * than the previous reads marked as valid. Otherwise this would return
1354 * stale data when different block sizes are used for the images. */
1355 cbThisRead = cbToRead;
1356
1357 /*
1358 * Try to read from the given image.
1359 * If the block is not allocated read from override chain if present.
1360 */
1361 rc = pCurrImage->Backend->pfnAsyncRead(pCurrImage->pBackendData,
1362 uOffset, cbThisRead,
1363 pIoCtx, &cbThisRead);
1364
1365 if (rc == VERR_VD_BLOCK_FREE)
1366 {
1367 while ( pCurrImage->pPrev != NULL
1368 && rc == VERR_VD_BLOCK_FREE)
1369 {
1370 pCurrImage = pCurrImage->pPrev;
1371 rc = pCurrImage->Backend->pfnAsyncRead(pCurrImage->pBackendData,
1372 uOffset, cbThisRead,
1373 pIoCtx, &cbThisRead);
1374 }
1375 }
1376
1377 /* The task state will be updated on success already, don't do it here!. */
1378 if (rc == VERR_VD_BLOCK_FREE)
1379 {
1380 /* No image in the chain contains the data for the block. */
1381 vdIoCtxSet(pIoCtx, '\0', cbThisRead);
1382 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbThisRead);
1383 rc = VINF_SUCCESS;
1384 }
1385 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1386 rc = VINF_SUCCESS;
1387 else if (rc == VERR_VD_IOCTX_HALT)
1388 {
1389 uOffset += cbThisRead;
1390 cbToRead -= cbThisRead;
1391 pIoCtx->fBlocked = true;
1392 }
1393
1394 if (RT_FAILURE(rc))
1395 break;
1396
1397 cbToRead -= cbThisRead;
1398 uOffset += cbThisRead;
1399 } while (cbToRead != 0 && RT_SUCCESS(rc));
1400
1401 if ( rc == VERR_VD_NOT_ENOUGH_METADATA
1402 || rc == VERR_VD_IOCTX_HALT)
1403 {
1404 /* Save the current state. */
1405 pIoCtx->uOffset = uOffset;
1406 pIoCtx->cbTransfer = cbToRead;
1407 pIoCtx->pImageCur = pCurrImage ? pCurrImage : pIoCtx->pImageStart;
1408 }
1409
1410 return rc;
1411}
1412
1413/**
1414 * internal: parent image read wrapper for compacting.
1415 */
1416static int vdParentRead(void *pvUser, uint64_t uOffset, void *pvBuf,
1417 size_t cbRead)
1418{
1419 PVDPARENTSTATEDESC pParentState = (PVDPARENTSTATEDESC)pvUser;
1420 return vdReadHelper(pParentState->pDisk, pParentState->pImage, uOffset,
1421 pvBuf, cbRead, false /* fUpdateCache */);
1422}
1423
1424/**
1425 * internal: mark the disk as not modified.
1426 */
1427static void vdResetModifiedFlag(PVBOXHDD pDisk)
1428{
1429 if (pDisk->uModified & VD_IMAGE_MODIFIED_FLAG)
1430 {
1431 /* generate new last-modified uuid */
1432 if (!(pDisk->uModified & VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
1433 {
1434 RTUUID Uuid;
1435
1436 RTUuidCreate(&Uuid);
1437 pDisk->pLast->Backend->pfnSetModificationUuid(pDisk->pLast->pBackendData,
1438 &Uuid);
1439
1440 if (pDisk->pCache)
1441 pDisk->pCache->Backend->pfnSetModificationUuid(pDisk->pCache->pBackendData,
1442 &Uuid);
1443 }
1444
1445 pDisk->uModified &= ~VD_IMAGE_MODIFIED_FLAG;
1446 }
1447}
1448
1449/**
1450 * internal: mark the disk as modified.
1451 */
1452static void vdSetModifiedFlag(PVBOXHDD pDisk)
1453{
1454 pDisk->uModified |= VD_IMAGE_MODIFIED_FLAG;
1455 if (pDisk->uModified & VD_IMAGE_MODIFIED_FIRST)
1456 {
1457 pDisk->uModified &= ~VD_IMAGE_MODIFIED_FIRST;
1458
1459 /* First modify, so create a UUID and ensure it's written to disk. */
1460 vdResetModifiedFlag(pDisk);
1461
1462 if (!(pDisk->uModified & VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
1463 pDisk->pLast->Backend->pfnFlush(pDisk->pLast->pBackendData);
1464 }
1465}
1466
1467/**
1468 * internal: write a complete block (only used for diff images), taking the
1469 * remaining data from parent images. This implementation does not optimize
1470 * anything (except that it tries to read only that portions from parent
1471 * images that are really needed).
1472 */
1473static int vdWriteHelperStandard(PVBOXHDD pDisk, PVDIMAGE pImage,
1474 PVDIMAGE pImageParentOverride,
1475 uint64_t uOffset, size_t cbWrite,
1476 size_t cbThisWrite, size_t cbPreRead,
1477 size_t cbPostRead, const void *pvBuf,
1478 void *pvTmp)
1479{
1480 int rc = VINF_SUCCESS;
1481
1482 /* Read the data that goes before the write to fill the block. */
1483 if (cbPreRead)
1484 {
1485 /*
1486 * Updating the cache doesn't make sense here because
1487 * this will be done after the complete block was written.
1488 */
1489 rc = vdReadHelperEx(pDisk, pImage, pImageParentOverride,
1490 uOffset - cbPreRead, pvTmp, cbPreRead,
1491 true /* fZeroFreeBlocks*/,
1492 false /* fUpdateCache */, 0);
1493 if (RT_FAILURE(rc))
1494 return rc;
1495 }
1496
1497 /* Copy the data to the right place in the buffer. */
1498 memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
1499
1500 /* Read the data that goes after the write to fill the block. */
1501 if (cbPostRead)
1502 {
1503 /* If we have data to be written, use that instead of reading
1504 * data from the image. */
1505 size_t cbWriteCopy;
1506 if (cbWrite > cbThisWrite)
1507 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
1508 else
1509 cbWriteCopy = 0;
1510 /* Figure out how much we cannot read from the image, because
1511 * the last block to write might exceed the nominal size of the
1512 * image for technical reasons. */
1513 size_t cbFill;
1514 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
1515 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
1516 else
1517 cbFill = 0;
1518 /* The rest must be read from the image. */
1519 size_t cbReadImage = cbPostRead - cbWriteCopy - cbFill;
1520
1521 /* Now assemble the remaining data. */
1522 if (cbWriteCopy)
1523 memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
1524 (char *)pvBuf + cbThisWrite, cbWriteCopy);
1525 if (cbReadImage)
1526 rc = vdReadHelperEx(pDisk, pImage, pImageParentOverride,
1527 uOffset + cbThisWrite + cbWriteCopy,
1528 (char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy,
1529 cbReadImage, true /* fZeroFreeBlocks */,
1530 false /* fUpdateCache */, 0);
1531 if (RT_FAILURE(rc))
1532 return rc;
1533 /* Zero out the remainder of this block. Will never be visible, as this
1534 * is beyond the limit of the image. */
1535 if (cbFill)
1536 memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
1537 '\0', cbFill);
1538 }
1539
1540 /* Write the full block to the virtual disk. */
1541 rc = pImage->Backend->pfnWrite(pImage->pBackendData,
1542 uOffset - cbPreRead, pvTmp,
1543 cbPreRead + cbThisWrite + cbPostRead,
1544 NULL, &cbPreRead, &cbPostRead, 0);
1545 Assert(rc != VERR_VD_BLOCK_FREE);
1546 Assert(cbPreRead == 0);
1547 Assert(cbPostRead == 0);
1548
1549 return rc;
1550}
1551
1552/**
1553 * internal: write a complete block (only used for diff images), taking the
1554 * remaining data from parent images. This implementation optimizes out writes
1555 * that do not change the data relative to the state as of the parent images.
1556 * All backends which support differential/growing images support this.
1557 */
1558static int vdWriteHelperOptimized(PVBOXHDD pDisk, PVDIMAGE pImage,
1559 PVDIMAGE pImageParentOverride,
1560 uint64_t uOffset, size_t cbWrite,
1561 size_t cbThisWrite, size_t cbPreRead,
1562 size_t cbPostRead, const void *pvBuf,
1563 void *pvTmp, unsigned cImagesRead)
1564{
1565 size_t cbFill = 0;
1566 size_t cbWriteCopy = 0;
1567 size_t cbReadImage = 0;
1568 int rc;
1569
1570 if (cbPostRead)
1571 {
1572 /* Figure out how much we cannot read from the image, because
1573 * the last block to write might exceed the nominal size of the
1574 * image for technical reasons. */
1575 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
1576 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
1577
1578 /* If we have data to be written, use that instead of reading
1579 * data from the image. */
1580 if (cbWrite > cbThisWrite)
1581 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
1582
1583 /* The rest must be read from the image. */
1584 cbReadImage = cbPostRead - cbWriteCopy - cbFill;
1585 }
1586
1587 /* Read the entire data of the block so that we can compare whether it will
1588 * be modified by the write or not. */
1589 rc = vdReadHelperEx(pDisk, pImage, pImageParentOverride, uOffset - cbPreRead, pvTmp,
1590 cbPreRead + cbThisWrite + cbPostRead - cbFill,
1591 true /* fZeroFreeBlocks */, false /* fUpdateCache */,
1592 cImagesRead);
1593 if (RT_FAILURE(rc))
1594 return rc;
1595
1596 /* Check if the write would modify anything in this block. */
1597 if ( !memcmp((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite)
1598 && (!cbWriteCopy || !memcmp((char *)pvTmp + cbPreRead + cbThisWrite,
1599 (char *)pvBuf + cbThisWrite, cbWriteCopy)))
1600 {
1601 /* Block is completely unchanged, so no need to write anything. */
1602 return VINF_SUCCESS;
1603 }
1604
1605 /* Copy the data to the right place in the buffer. */
1606 memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
1607
1608 /* Handle the data that goes after the write to fill the block. */
1609 if (cbPostRead)
1610 {
1611 /* Now assemble the remaining data. */
1612 if (cbWriteCopy)
1613 memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
1614 (char *)pvBuf + cbThisWrite, cbWriteCopy);
1615 /* Zero out the remainder of this block. Will never be visible, as this
1616 * is beyond the limit of the image. */
1617 if (cbFill)
1618 memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
1619 '\0', cbFill);
1620 }
1621
1622 /* Write the full block to the virtual disk. */
1623 rc = pImage->Backend->pfnWrite(pImage->pBackendData,
1624 uOffset - cbPreRead, pvTmp,
1625 cbPreRead + cbThisWrite + cbPostRead,
1626 NULL, &cbPreRead, &cbPostRead, 0);
1627 Assert(rc != VERR_VD_BLOCK_FREE);
1628 Assert(cbPreRead == 0);
1629 Assert(cbPostRead == 0);
1630
1631 return rc;
1632}
1633
1634/**
1635 * internal: write buffer to the image, taking care of block boundaries and
1636 * write optimizations.
1637 */
1638static int vdWriteHelperEx(PVBOXHDD pDisk, PVDIMAGE pImage,
1639 PVDIMAGE pImageParentOverride, uint64_t uOffset,
1640 const void *pvBuf, size_t cbWrite,
1641 bool fUpdateCache, unsigned cImagesRead)
1642{
1643 int rc;
1644 unsigned fWrite;
1645 size_t cbThisWrite;
1646 size_t cbPreRead, cbPostRead;
1647 uint64_t uOffsetCur = uOffset;
1648 size_t cbWriteCur = cbWrite;
1649 const void *pcvBufCur = pvBuf;
1650
1651 /* Loop until all written. */
1652 do
1653 {
1654 /* Try to write the possibly partial block to the last opened image.
1655 * This works when the block is already allocated in this image or
1656 * if it is a full-block write (and allocation isn't suppressed below).
1657 * For image formats which don't support zero blocks, it's beneficial
1658 * to avoid unnecessarily allocating unchanged blocks. This prevents
1659 * unwanted expanding of images. VMDK is an example. */
1660 cbThisWrite = cbWriteCur;
1661 fWrite = (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
1662 ? 0 : VD_WRITE_NO_ALLOC;
1663 rc = pImage->Backend->pfnWrite(pImage->pBackendData, uOffsetCur, pcvBufCur,
1664 cbThisWrite, &cbThisWrite, &cbPreRead,
1665 &cbPostRead, fWrite);
1666 if (rc == VERR_VD_BLOCK_FREE)
1667 {
1668 void *pvTmp = RTMemTmpAlloc(cbPreRead + cbThisWrite + cbPostRead);
1669 AssertBreakStmt(VALID_PTR(pvTmp), rc = VERR_NO_MEMORY);
1670
1671 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME))
1672 {
1673 /* Optimized write, suppress writing to a so far unallocated
1674 * block if the data is in fact not changed. */
1675 rc = vdWriteHelperOptimized(pDisk, pImage, pImageParentOverride,
1676 uOffsetCur, cbWriteCur,
1677 cbThisWrite, cbPreRead, cbPostRead,
1678 pcvBufCur, pvTmp, cImagesRead);
1679 }
1680 else
1681 {
1682 /* Normal write, not optimized in any way. The block will
1683 * be written no matter what. This will usually (unless the
1684 * backend has some further optimization enabled) cause the
1685 * block to be allocated. */
1686 rc = vdWriteHelperStandard(pDisk, pImage, pImageParentOverride,
1687 uOffsetCur, cbWriteCur,
1688 cbThisWrite, cbPreRead, cbPostRead,
1689 pcvBufCur, pvTmp);
1690 }
1691 RTMemTmpFree(pvTmp);
1692 if (RT_FAILURE(rc))
1693 break;
1694 }
1695
1696 cbWriteCur -= cbThisWrite;
1697 uOffsetCur += cbThisWrite;
1698 pcvBufCur = (char *)pcvBufCur + cbThisWrite;
1699 } while (cbWriteCur != 0 && RT_SUCCESS(rc));
1700
1701 /* Update the cache on success */
1702 if ( RT_SUCCESS(rc)
1703 && pDisk->pCache
1704 && fUpdateCache)
1705 rc = vdCacheWriteHelper(pDisk->pCache, uOffset, pvBuf, cbWrite, NULL);
1706
1707 return rc;
1708}
1709
1710/**
1711 * internal: write buffer to the image, taking care of block boundaries and
1712 * write optimizations.
1713 */
1714static int vdWriteHelper(PVBOXHDD pDisk, PVDIMAGE pImage, uint64_t uOffset,
1715 const void *pvBuf, size_t cbWrite, bool fUpdateCache)
1716{
1717 return vdWriteHelperEx(pDisk, pImage, NULL, uOffset, pvBuf, cbWrite,
1718 fUpdateCache, 0);
1719}
1720
1721/**
1722 * Internal: Copies the content of one disk to another one applying optimizations
1723 * to speed up the copy process if possible.
1724 */
1725static int vdCopyHelper(PVBOXHDD pDiskFrom, PVDIMAGE pImageFrom, PVBOXHDD pDiskTo,
1726 uint64_t cbSize, unsigned cImagesFromRead, unsigned cImagesToRead,
1727 bool fSuppressRedundantIo,
1728 PVDINTERFACE pIfProgress, PVDINTERFACEPROGRESS pCbProgress,
1729 PVDINTERFACE pDstIfProgress, PVDINTERFACEPROGRESS pDstCbProgress)
1730{
1731 int rc = VINF_SUCCESS;
1732 int rc2;
1733 uint64_t uOffset = 0;
1734 uint64_t cbRemaining = cbSize;
1735 void *pvBuf = NULL;
1736 bool fLockReadFrom = false;
1737 bool fLockWriteTo = false;
1738 bool fBlockwiseCopy = fSuppressRedundantIo || (cImagesFromRead > 0);
1739 unsigned uProgressOld = 0;
1740
1741 LogFlowFunc(("pDiskFrom=%#p pImageFrom=%#p pDiskTo=%#p cbSize=%llu cImagesFromRead=%u cImagesToRead=%u fSuppressRedundantIo=%RTbool pIfProgress=%#p pCbProgress=%#p pDstIfProgress=%#p pDstCbProgress=%#p\n",
1742 pDiskFrom, pImageFrom, pDiskTo, cbSize, cImagesFromRead, cImagesToRead, fSuppressRedundantIo, pIfProgress, pCbProgress, pDstIfProgress, pDstCbProgress));
1743
1744 /* Allocate tmp buffer. */
1745 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
1746 if (!pvBuf)
1747 return rc;
1748
1749 do
1750 {
1751 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
1752
1753 /* Note that we don't attempt to synchronize cross-disk accesses.
1754 * It wouldn't be very difficult to do, just the lock order would
1755 * need to be defined somehow to prevent deadlocks. Postpone such
1756 * magic as there is no use case for this. */
1757
1758 rc2 = vdThreadStartRead(pDiskFrom);
1759 AssertRC(rc2);
1760 fLockReadFrom = true;
1761
1762 if (fBlockwiseCopy)
1763 {
1764 /* Read the source data. */
1765 rc = pImageFrom->Backend->pfnRead(pImageFrom->pBackendData,
1766 uOffset, pvBuf, cbThisRead,
1767 &cbThisRead);
1768
1769 if ( rc == VERR_VD_BLOCK_FREE
1770 && cImagesFromRead != 1)
1771 {
1772 unsigned cImagesToProcess = cImagesFromRead;
1773
1774 for (PVDIMAGE pCurrImage = pImageFrom->pPrev;
1775 pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
1776 pCurrImage = pCurrImage->pPrev)
1777 {
1778 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
1779 uOffset, pvBuf, cbThisRead,
1780 &cbThisRead);
1781 if (cImagesToProcess == 1)
1782 break;
1783 else if (cImagesToProcess > 0)
1784 cImagesToProcess--;
1785 }
1786 }
1787 }
1788 else
1789 rc = vdReadHelper(pDiskFrom, pImageFrom, uOffset, pvBuf, cbThisRead,
1790 false /* fUpdateCache */);
1791
1792 if (RT_FAILURE(rc) && rc != VERR_VD_BLOCK_FREE)
1793 break;
1794
1795 rc2 = vdThreadFinishRead(pDiskFrom);
1796 AssertRC(rc2);
1797 fLockReadFrom = false;
1798
1799 if (rc != VERR_VD_BLOCK_FREE)
1800 {
1801 rc2 = vdThreadStartWrite(pDiskTo);
1802 AssertRC(rc2);
1803 fLockWriteTo = true;
1804
1805 /* Only do collapsed I/O if we are copying the data blockwise. */
1806 rc = vdWriteHelperEx(pDiskTo, pDiskTo->pLast, NULL, uOffset, pvBuf,
1807 cbThisRead, false /* fUpdateCache */,
1808 fBlockwiseCopy ? cImagesToRead : 0);
1809 if (RT_FAILURE(rc))
1810 break;
1811
1812 rc2 = vdThreadFinishWrite(pDiskTo);
1813 AssertRC(rc2);
1814 fLockWriteTo = false;
1815 }
1816 else /* Don't propagate the error to the outside */
1817 rc = VINF_SUCCESS;
1818
1819 uOffset += cbThisRead;
1820 cbRemaining -= cbThisRead;
1821
1822 unsigned uProgressNew = uOffset * 99 / cbSize;
1823 if (uProgressNew != uProgressOld)
1824 {
1825 uProgressOld = uProgressNew;
1826
1827 if (pCbProgress && pCbProgress->pfnProgress)
1828 {
1829 rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
1830 uProgressOld);
1831 if (RT_FAILURE(rc))
1832 break;
1833 }
1834 if (pDstCbProgress && pDstCbProgress->pfnProgress)
1835 {
1836 rc = pDstCbProgress->pfnProgress(pDstIfProgress->pvUser,
1837 uProgressOld);
1838 if (RT_FAILURE(rc))
1839 break;
1840 }
1841 }
1842 } while (uOffset < cbSize);
1843
1844 RTMemFree(pvBuf);
1845
1846 if (fLockReadFrom)
1847 {
1848 rc2 = vdThreadFinishRead(pDiskFrom);
1849 AssertRC(rc2);
1850 }
1851
1852 if (fLockWriteTo)
1853 {
1854 rc2 = vdThreadFinishWrite(pDiskTo);
1855 AssertRC(rc2);
1856 }
1857
1858 LogFlowFunc(("returns rc=%Rrc\n", rc));
1859 return rc;
1860}
1861
1862/**
1863 * Flush helper async version.
1864 */
1865static int vdSetModifiedHelperAsync(PVDIOCTX pIoCtx)
1866{
1867 int rc = VINF_SUCCESS;
1868 PVBOXHDD pDisk = pIoCtx->pDisk;
1869 PVDIMAGE pImage = pIoCtx->pImageCur;
1870
1871 rc = pImage->Backend->pfnAsyncFlush(pImage->pBackendData, pIoCtx);
1872 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1873 rc = VINF_SUCCESS;
1874
1875 return rc;
1876}
1877
1878/**
1879 * internal: mark the disk as modified - async version.
1880 */
1881static int vdSetModifiedFlagAsync(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
1882{
1883 int rc = VINF_SUCCESS;
1884
1885 pDisk->uModified |= VD_IMAGE_MODIFIED_FLAG;
1886 if (pDisk->uModified & VD_IMAGE_MODIFIED_FIRST)
1887 {
1888 rc = vdIoCtxLockDisk(pDisk, pIoCtx);
1889 if (RT_SUCCESS(rc))
1890 {
1891 pDisk->uModified &= ~VD_IMAGE_MODIFIED_FIRST;
1892
1893 /* First modify, so create a UUID and ensure it's written to disk. */
1894 vdResetModifiedFlag(pDisk);
1895
1896 if (!(pDisk->uModified & VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
1897 {
1898 PVDIOCTX pIoCtxFlush = vdIoCtxChildAlloc(pDisk, VDIOCTXTXDIR_FLUSH,
1899 0, 0, pDisk->pLast,
1900 NULL, pIoCtx, 0, 0, NULL,
1901 vdSetModifiedHelperAsync);
1902
1903 if (pIoCtxFlush)
1904 {
1905 rc = vdIoCtxProcess(pIoCtxFlush);
1906 if (rc == VINF_VD_ASYNC_IO_FINISHED)
1907 {
1908 vdIoCtxUnlockDisk(pDisk, pIoCtx, false /* fProcessDeferredReqs */);
1909 vdIoCtxFree(pDisk, pIoCtxFlush);
1910 }
1911 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1912 {
1913 pIoCtx->fBlocked = true;
1914 }
1915 else /* Another error */
1916 vdIoCtxFree(pDisk, pIoCtxFlush);
1917 }
1918 else
1919 rc = VERR_NO_MEMORY;
1920 }
1921 }
1922 }
1923
1924 return rc;
1925}
1926
1927/**
1928 * internal: write a complete block (only used for diff images), taking the
1929 * remaining data from parent images. This implementation does not optimize
1930 * anything (except that it tries to read only that portions from parent
1931 * images that are really needed) - async version.
1932 */
1933static int vdWriteHelperStandardAsync(PVDIOCTX pIoCtx)
1934{
1935 int rc = VINF_SUCCESS;
1936
1937#if 0
1938
1939 /* Read the data that goes before the write to fill the block. */
1940 if (cbPreRead)
1941 {
1942 rc = vdReadHelperAsync(pIoCtxDst);
1943 if (RT_FAILURE(rc))
1944 return rc;
1945 }
1946
1947 /* Copy the data to the right place in the buffer. */
1948 vdIoCtxCopy(pIoCtxDst, pIoCtxSrc, cbThisWrite);
1949
1950 /* Read the data that goes after the write to fill the block. */
1951 if (cbPostRead)
1952 {
1953 /* If we have data to be written, use that instead of reading
1954 * data from the image. */
1955 size_t cbWriteCopy;
1956 if (cbWrite > cbThisWrite)
1957 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
1958 else
1959 cbWriteCopy = 0;
1960 /* Figure out how much we cannot read from the image, because
1961 * the last block to write might exceed the nominal size of the
1962 * image for technical reasons. */
1963 size_t cbFill;
1964 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
1965 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
1966 else
1967 cbFill = 0;
1968 /* The rest must be read from the image. */
1969 size_t cbReadImage = cbPostRead - cbWriteCopy - cbFill;
1970
1971 /* Now assemble the remaining data. */
1972 if (cbWriteCopy)
1973 {
1974 vdIoCtxCopy(pIoCtxDst, pIoCtxSrc, cbWriteCopy);
1975 ASMAtomicSubU32(&pIoCtxDst->cbTransferLeft, cbWriteCopy);
1976 }
1977
1978 if (cbReadImage)
1979 rc = vdReadHelperAsync(pDisk, pImage, pImageParentOverride, pIoCtxDst,
1980 uOffset + cbThisWrite + cbWriteCopy,
1981 cbReadImage);
1982 if (RT_FAILURE(rc))
1983 return rc;
1984 /* Zero out the remainder of this block. Will never be visible, as this
1985 * is beyond the limit of the image. */
1986 if (cbFill)
1987 {
1988 vdIoCtxSet(pIoCtxDst, '\0', cbFill);
1989 ASMAtomicSubU32(&pIoCtxDst->cbTransferLeft, cbFill);
1990 }
1991 }
1992
1993 if ( !pIoCtxDst->cbTransferLeft
1994 && !pIoCtxDst->cMetaTransfersPending
1995 && ASMAtomicCmpXchgBool(&pIoCtxDst->fComplete, true, false))
1996 {
1997 /* Write the full block to the virtual disk. */
1998 vdIoCtxChildReset(pIoCtxDst);
1999 rc = pImage->Backend->pfnAsyncWrite(pImage->pBackendData,
2000 uOffset - cbPreRead,
2001 cbPreRead + cbThisWrite + cbPostRead,
2002 pIoCtxDst,
2003 NULL, &cbPreRead, &cbPostRead, 0);
2004 Assert(rc != VERR_VD_BLOCK_FREE);
2005 Assert(cbPreRead == 0);
2006 Assert(cbPostRead == 0);
2007 }
2008 else
2009 {
2010 LogFlow(("cbTransferLeft=%u cMetaTransfersPending=%u fComplete=%RTbool\n",
2011 pIoCtxDst->cbTransferLeft, pIoCtxDst->cMetaTransfersPending,
2012 pIoCtxDst->fComplete));
2013 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2014 }
2015
2016 return rc;
2017#endif
2018 return VERR_NOT_IMPLEMENTED;
2019}
2020
2021static int vdWriteHelperOptimizedCommitAsync(PVDIOCTX pIoCtx)
2022{
2023 int rc = VINF_SUCCESS;
2024 PVDIMAGE pImage = pIoCtx->pImageStart;
2025 size_t cbPreRead = pIoCtx->Type.Child.cbPreRead;
2026 size_t cbPostRead = pIoCtx->Type.Child.cbPostRead;
2027 size_t cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
2028
2029 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
2030 rc = pImage->Backend->pfnAsyncWrite(pImage->pBackendData,
2031 pIoCtx->uOffset - cbPreRead,
2032 cbPreRead + cbThisWrite + cbPostRead,
2033 pIoCtx, NULL, &cbPreRead, &cbPostRead, 0);
2034 Assert(rc != VERR_VD_BLOCK_FREE);
2035 Assert(rc == VERR_VD_NOT_ENOUGH_METADATA || cbPreRead == 0);
2036 Assert(rc == VERR_VD_NOT_ENOUGH_METADATA || cbPostRead == 0);
2037 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2038 rc = VINF_SUCCESS;
2039 else if (rc == VERR_VD_IOCTX_HALT)
2040 {
2041 pIoCtx->fBlocked = true;
2042 rc = VINF_SUCCESS;
2043 }
2044
2045 LogFlowFunc(("returns rc=%Rrc\n", rc));
2046 return rc;
2047}
2048
2049static int vdWriteHelperOptimizedCmpAndWriteAsync(PVDIOCTX pIoCtx)
2050{
2051 int rc = VINF_SUCCESS;
2052 PVDIMAGE pImage = pIoCtx->pImageCur;
2053 size_t cbThisWrite = 0;
2054 size_t cbPreRead = pIoCtx->Type.Child.cbPreRead;
2055 size_t cbPostRead = pIoCtx->Type.Child.cbPostRead;
2056 size_t cbWriteCopy = pIoCtx->Type.Child.Write.Optimized.cbWriteCopy;
2057 size_t cbFill = pIoCtx->Type.Child.Write.Optimized.cbFill;
2058 size_t cbReadImage = pIoCtx->Type.Child.Write.Optimized.cbReadImage;
2059 PVDIOCTX pIoCtxParent = pIoCtx->pIoCtxParent;
2060
2061 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
2062
2063 AssertPtr(pIoCtxParent);
2064 Assert(!pIoCtxParent->pIoCtxParent);
2065 Assert(!pIoCtx->cbTransferLeft && !pIoCtx->cMetaTransfersPending);
2066
2067 vdIoCtxChildReset(pIoCtx);
2068 cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
2069 RTSgBufAdvance(&pIoCtx->SgBuf, cbPreRead);
2070
2071 /* Check if the write would modify anything in this block. */
2072 if (!RTSgBufCmp(&pIoCtx->SgBuf, &pIoCtxParent->SgBuf, cbThisWrite))
2073 {
2074 RTSGBUF SgBufSrcTmp;
2075
2076 RTSgBufClone(&SgBufSrcTmp, &pIoCtxParent->SgBuf);
2077 RTSgBufAdvance(&SgBufSrcTmp, cbThisWrite);
2078 RTSgBufAdvance(&pIoCtx->SgBuf, cbThisWrite);
2079
2080 if (!cbWriteCopy || !RTSgBufCmp(&pIoCtx->SgBuf, &SgBufSrcTmp, cbWriteCopy))
2081 {
2082 /* Block is completely unchanged, so no need to write anything. */
2083 LogFlowFunc(("Block didn't changed\n"));
2084 ASMAtomicWriteU32(&pIoCtx->cbTransferLeft, 0);
2085 RTSgBufAdvance(&pIoCtxParent->SgBuf, cbThisWrite);
2086 return VINF_VD_ASYNC_IO_FINISHED;
2087 }
2088 }
2089
2090 /* Copy the data to the right place in the buffer. */
2091 RTSgBufReset(&pIoCtx->SgBuf);
2092 RTSgBufAdvance(&pIoCtx->SgBuf, cbPreRead);
2093 vdIoCtxCopy(pIoCtx, pIoCtxParent, cbThisWrite);
2094
2095 /* Handle the data that goes after the write to fill the block. */
2096 if (cbPostRead)
2097 {
2098 /* Now assemble the remaining data. */
2099 if (cbWriteCopy)
2100 {
2101 /*
2102 * The S/G buffer of the parent needs to be cloned because
2103 * it is not allowed to modify the state.
2104 */
2105 RTSGBUF SgBufParentTmp;
2106
2107 RTSgBufClone(&SgBufParentTmp, &pIoCtxParent->SgBuf);
2108 RTSgBufCopy(&pIoCtx->SgBuf, &SgBufParentTmp, cbWriteCopy);
2109 }
2110
2111 /* Zero out the remainder of this block. Will never be visible, as this
2112 * is beyond the limit of the image. */
2113 if (cbFill)
2114 {
2115 RTSgBufAdvance(&pIoCtx->SgBuf, cbReadImage);
2116 vdIoCtxSet(pIoCtx, '\0', cbFill);
2117 }
2118 }
2119
2120 /* Write the full block to the virtual disk. */
2121 RTSgBufReset(&pIoCtx->SgBuf);
2122 pIoCtx->pfnIoCtxTransferNext = vdWriteHelperOptimizedCommitAsync;
2123
2124 return rc;
2125}
2126
2127static int vdWriteHelperOptimizedPreReadAsync(PVDIOCTX pIoCtx)
2128{
2129 int rc = VINF_SUCCESS;
2130
2131 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
2132
2133 if (pIoCtx->cbTransferLeft)
2134 rc = vdReadHelperAsync(pIoCtx);
2135
2136 if ( RT_SUCCESS(rc)
2137 && ( pIoCtx->cbTransferLeft
2138 || pIoCtx->cMetaTransfersPending))
2139 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2140 else
2141 pIoCtx->pfnIoCtxTransferNext = vdWriteHelperOptimizedCmpAndWriteAsync;
2142
2143 return rc;
2144}
2145
2146/**
2147 * internal: write a complete block (only used for diff images), taking the
2148 * remaining data from parent images. This implementation optimizes out writes
2149 * that do not change the data relative to the state as of the parent images.
2150 * All backends which support differential/growing images support this - async version.
2151 */
2152static int vdWriteHelperOptimizedAsync(PVDIOCTX pIoCtx)
2153{
2154 PVBOXHDD pDisk = pIoCtx->pDisk;
2155 uint64_t uOffset = pIoCtx->Type.Child.uOffsetSaved;
2156 size_t cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
2157 size_t cbPreRead = pIoCtx->Type.Child.cbPreRead;
2158 size_t cbPostRead = pIoCtx->Type.Child.cbPostRead;
2159 size_t cbWrite = pIoCtx->Type.Child.cbWriteParent;
2160 size_t cbFill = 0;
2161 size_t cbWriteCopy = 0;
2162 size_t cbReadImage = 0;
2163
2164 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
2165
2166 AssertPtr(pIoCtx->pIoCtxParent);
2167 Assert(!pIoCtx->pIoCtxParent->pIoCtxParent);
2168
2169 if (cbPostRead)
2170 {
2171 /* Figure out how much we cannot read from the image, because
2172 * the last block to write might exceed the nominal size of the
2173 * image for technical reasons. */
2174 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
2175 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
2176
2177 /* If we have data to be written, use that instead of reading
2178 * data from the image. */
2179 if (cbWrite > cbThisWrite)
2180 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
2181
2182 /* The rest must be read from the image. */
2183 cbReadImage = cbPostRead - cbWriteCopy - cbFill;
2184 }
2185
2186 pIoCtx->Type.Child.Write.Optimized.cbFill = cbFill;
2187 pIoCtx->Type.Child.Write.Optimized.cbWriteCopy = cbWriteCopy;
2188 pIoCtx->Type.Child.Write.Optimized.cbReadImage = cbReadImage;
2189
2190 /* Read the entire data of the block so that we can compare whether it will
2191 * be modified by the write or not. */
2192 pIoCtx->cbTransferLeft = cbPreRead + cbThisWrite + cbPostRead - cbFill;
2193 pIoCtx->cbTransfer = pIoCtx->cbTransferLeft;
2194 pIoCtx->uOffset -= cbPreRead;
2195
2196 /* Next step */
2197 pIoCtx->pfnIoCtxTransferNext = vdWriteHelperOptimizedPreReadAsync;
2198 return VINF_SUCCESS;
2199}
2200
2201/**
2202 * internal: write buffer to the image, taking care of block boundaries and
2203 * write optimizations - async version.
2204 */
2205static int vdWriteHelperAsync(PVDIOCTX pIoCtx)
2206{
2207 int rc;
2208 size_t cbWrite = pIoCtx->cbTransfer;
2209 uint64_t uOffset = pIoCtx->uOffset;
2210 PVDIMAGE pImage = pIoCtx->pImageCur;
2211 PVBOXHDD pDisk = pIoCtx->pDisk;
2212 unsigned fWrite;
2213 size_t cbThisWrite;
2214 size_t cbPreRead, cbPostRead;
2215
2216 rc = vdSetModifiedFlagAsync(pDisk, pIoCtx);
2217 if (RT_FAILURE(rc)) /* Includes I/O in progress. */
2218 return rc;
2219
2220 /* Loop until all written. */
2221 do
2222 {
2223 /* Try to write the possibly partial block to the last opened image.
2224 * This works when the block is already allocated in this image or
2225 * if it is a full-block write (and allocation isn't suppressed below).
2226 * For image formats which don't support zero blocks, it's beneficial
2227 * to avoid unnecessarily allocating unchanged blocks. This prevents
2228 * unwanted expanding of images. VMDK is an example. */
2229 cbThisWrite = cbWrite;
2230 fWrite = (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
2231 ? 0 : VD_WRITE_NO_ALLOC;
2232 rc = pImage->Backend->pfnAsyncWrite(pImage->pBackendData, uOffset,
2233 cbThisWrite, pIoCtx,
2234 &cbThisWrite, &cbPreRead,
2235 &cbPostRead, fWrite);
2236 if (rc == VERR_VD_BLOCK_FREE)
2237 {
2238 /* Lock the disk .*/
2239 rc = vdIoCtxLockDisk(pDisk, pIoCtx);
2240 if (RT_SUCCESS(rc))
2241 {
2242 /*
2243 * Allocate segment and buffer in one go.
2244 * A bit hackish but avoids the need to allocate memory twice.
2245 */
2246 PRTSGBUF pTmp = (PRTSGBUF)RTMemAlloc(cbPreRead + cbThisWrite + cbPostRead + sizeof(RTSGSEG) + sizeof(RTSGBUF));
2247 AssertBreakStmt(VALID_PTR(pTmp), rc = VERR_NO_MEMORY);
2248 PRTSGSEG pSeg = (PRTSGSEG)(pTmp + 1);
2249
2250 pSeg->pvSeg = pSeg + 1;
2251 pSeg->cbSeg = cbPreRead + cbThisWrite + cbPostRead;
2252 RTSgBufInit(pTmp, pSeg, 1);
2253
2254 PVDIOCTX pIoCtxWrite = vdIoCtxChildAlloc(pDisk, VDIOCTXTXDIR_WRITE,
2255 uOffset, pSeg->cbSeg, pImage,
2256 pTmp,
2257 pIoCtx, cbThisWrite,
2258 cbWrite,
2259 pTmp,
2260 (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
2261 ? vdWriteHelperStandardAsync
2262 : vdWriteHelperOptimizedAsync);
2263 if (!VALID_PTR(pIoCtxWrite))
2264 {
2265 RTMemTmpFree(pTmp);
2266 rc = VERR_NO_MEMORY;
2267 break;
2268 }
2269
2270 LogFlowFunc(("Disk is growing because of pIoCtx=%#p pIoCtxWrite=%#p\n",
2271 pIoCtx, pIoCtxWrite));
2272
2273 pIoCtxWrite->Type.Child.cbPreRead = cbPreRead;
2274 pIoCtxWrite->Type.Child.cbPostRead = cbPostRead;
2275
2276 /* Process the write request */
2277 rc = vdIoCtxProcess(pIoCtxWrite);
2278
2279 if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
2280 {
2281 vdIoCtxFree(pDisk, pIoCtxWrite);
2282 break;
2283 }
2284 else if ( rc == VINF_VD_ASYNC_IO_FINISHED
2285 && ASMAtomicCmpXchgBool(&pIoCtxWrite->fComplete, true, false))
2286 {
2287 LogFlow(("Child write request completed\n"));
2288 Assert(pIoCtx->cbTransferLeft >= cbThisWrite);
2289 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbThisWrite);
2290 vdIoCtxUnlockDisk(pDisk, pIoCtx, false /* fProcessDeferredReqs*/ );
2291 vdIoCtxFree(pDisk, pIoCtxWrite);
2292
2293 rc = VINF_SUCCESS;
2294 }
2295 else
2296 {
2297 LogFlow(("Child write pending\n"));
2298 pIoCtx->fBlocked = true;
2299 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2300 cbWrite -= cbThisWrite;
2301 uOffset += cbThisWrite;
2302 break;
2303 }
2304 }
2305 else
2306 {
2307 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2308 break;
2309 }
2310 }
2311
2312 if (rc == VERR_VD_IOCTX_HALT)
2313 {
2314 cbWrite -= cbThisWrite;
2315 uOffset += cbThisWrite;
2316 pIoCtx->fBlocked = true;
2317 break;
2318 }
2319 else if (rc == VERR_VD_NOT_ENOUGH_METADATA)
2320 break;
2321
2322 cbWrite -= cbThisWrite;
2323 uOffset += cbThisWrite;
2324 } while (cbWrite != 0 && (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS));
2325
2326 if ( rc == VERR_VD_ASYNC_IO_IN_PROGRESS
2327 || rc == VERR_VD_NOT_ENOUGH_METADATA
2328 || rc == VERR_VD_IOCTX_HALT)
2329 {
2330 /*
2331 * Tell the caller that we don't need to go back here because all
2332 * writes are initiated.
2333 */
2334 if (!cbWrite)
2335 rc = VINF_SUCCESS;
2336
2337 pIoCtx->uOffset = uOffset;
2338 pIoCtx->cbTransfer = cbWrite;
2339 }
2340
2341 return rc;
2342}
2343
2344/**
2345 * Flush helper async version.
2346 */
2347static int vdFlushHelperAsync(PVDIOCTX pIoCtx)
2348{
2349 int rc = VINF_SUCCESS;
2350 PVBOXHDD pDisk = pIoCtx->pDisk;
2351 PVDIMAGE pImage = pIoCtx->pImageCur;
2352
2353 rc = vdIoCtxLockDisk(pDisk, pIoCtx);
2354 if (RT_SUCCESS(rc))
2355 {
2356 vdResetModifiedFlag(pDisk);
2357 rc = pImage->Backend->pfnAsyncFlush(pImage->pBackendData, pIoCtx);
2358 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2359 rc = VINF_SUCCESS;
2360 else if (rc == VINF_VD_ASYNC_IO_FINISHED)
2361 vdIoCtxUnlockDisk(pDisk, pIoCtx, true /* fProcessDeferredReqs */);
2362 }
2363
2364 return rc;
2365}
2366
2367/**
2368 * internal: scans plugin directory and loads the backends have been found.
2369 */
2370static int vdLoadDynamicBackends()
2371{
2372#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
2373 int rc = VINF_SUCCESS;
2374 PRTDIR pPluginDir = NULL;
2375
2376 /* Enumerate plugin backends. */
2377 char szPath[RTPATH_MAX];
2378 rc = RTPathAppPrivateArch(szPath, sizeof(szPath));
2379 if (RT_FAILURE(rc))
2380 return rc;
2381
2382 /* To get all entries with VBoxHDD as prefix. */
2383 char *pszPluginFilter = RTPathJoinA(szPath, VBOX_HDDFORMAT_PLUGIN_PREFIX "*");
2384 if (!pszPluginFilter)
2385 return VERR_NO_STR_MEMORY;
2386
2387 PRTDIRENTRYEX pPluginDirEntry = NULL;
2388 size_t cbPluginDirEntry = sizeof(RTDIRENTRYEX);
2389 /* The plugins are in the same directory as the other shared libs. */
2390 rc = RTDirOpenFiltered(&pPluginDir, pszPluginFilter, RTDIRFILTER_WINNT);
2391 if (RT_FAILURE(rc))
2392 {
2393 /* On Windows the above immediately signals that there are no
2394 * files matching, while on other platforms enumerating the
2395 * files below fails. Either way: no plugins. */
2396 goto out;
2397 }
2398
2399 pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
2400 if (!pPluginDirEntry)
2401 {
2402 rc = VERR_NO_MEMORY;
2403 goto out;
2404 }
2405
2406 while ((rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK)) != VERR_NO_MORE_FILES)
2407 {
2408 RTLDRMOD hPlugin = NIL_RTLDRMOD;
2409 PFNVBOXHDDFORMATLOAD pfnHDDFormatLoad = NULL;
2410 PVBOXHDDBACKEND pBackend = NULL;
2411 char *pszPluginPath = NULL;
2412
2413 if (rc == VERR_BUFFER_OVERFLOW)
2414 {
2415 /* allocate new buffer. */
2416 RTMemFree(pPluginDirEntry);
2417 pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbPluginDirEntry);
2418 if (!pPluginDirEntry)
2419 {
2420 rc = VERR_NO_MEMORY;
2421 break;
2422 }
2423 /* Retry. */
2424 rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
2425 if (RT_FAILURE(rc))
2426 break;
2427 }
2428 else if (RT_FAILURE(rc))
2429 break;
2430
2431 /* We got the new entry. */
2432 if (!RTFS_IS_FILE(pPluginDirEntry->Info.Attr.fMode))
2433 continue;
2434
2435 /* Prepend the path to the libraries. */
2436 pszPluginPath = RTPathJoinA(szPath, pPluginDirEntry->szName);
2437 if (!pszPluginPath)
2438 {
2439 rc = VERR_NO_STR_MEMORY;
2440 break;
2441 }
2442
2443 rc = SUPR3HardenedLdrLoadPlugIn(pszPluginPath, &hPlugin, NULL);
2444 if (RT_SUCCESS(rc))
2445 {
2446 rc = RTLdrGetSymbol(hPlugin, VBOX_HDDFORMAT_LOAD_NAME, (void**)&pfnHDDFormatLoad);
2447 if (RT_FAILURE(rc) || !pfnHDDFormatLoad)
2448 {
2449 LogFunc(("error resolving the entry point %s in plugin %s, rc=%Rrc, pfnHDDFormat=%#p\n", VBOX_HDDFORMAT_LOAD_NAME, pPluginDirEntry->szName, rc, pfnHDDFormatLoad));
2450 if (RT_SUCCESS(rc))
2451 rc = VERR_SYMBOL_NOT_FOUND;
2452 }
2453
2454 if (RT_SUCCESS(rc))
2455 {
2456 /* Get the function table. */
2457 rc = pfnHDDFormatLoad(&pBackend);
2458 if (RT_SUCCESS(rc) && pBackend->cbSize == sizeof(VBOXHDDBACKEND))
2459 {
2460 pBackend->hPlugin = hPlugin;
2461 vdAddBackend(pBackend);
2462 }
2463 else
2464 LogFunc(("ignored plugin '%s': pBackend->cbSize=%d rc=%Rrc\n", pszPluginPath, pBackend->cbSize, rc));
2465 }
2466 else
2467 LogFunc(("ignored plugin '%s': rc=%Rrc\n", pszPluginPath, rc));
2468
2469 if (RT_FAILURE(rc))
2470 RTLdrClose(hPlugin);
2471 }
2472 RTStrFree(pszPluginPath);
2473 }
2474out:
2475 if (rc == VERR_NO_MORE_FILES)
2476 rc = VINF_SUCCESS;
2477 RTStrFree(pszPluginFilter);
2478 if (pPluginDirEntry)
2479 RTMemFree(pPluginDirEntry);
2480 if (pPluginDir)
2481 RTDirClose(pPluginDir);
2482 return rc;
2483#else
2484 return VINF_SUCCESS;
2485#endif
2486}
2487
2488/**
2489 * internal: scans plugin directory and loads the cache backends have been found.
2490 */
2491static int vdLoadDynamicCacheBackends()
2492{
2493#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
2494 int rc = VINF_SUCCESS;
2495 PRTDIR pPluginDir = NULL;
2496
2497 /* Enumerate plugin backends. */
2498 char szPath[RTPATH_MAX];
2499 rc = RTPathAppPrivateArch(szPath, sizeof(szPath));
2500 if (RT_FAILURE(rc))
2501 return rc;
2502
2503 /* To get all entries with VBoxHDD as prefix. */
2504 char *pszPluginFilter = RTPathJoinA(szPath, VD_CACHEFORMAT_PLUGIN_PREFIX "*");
2505 if (!pszPluginFilter)
2506 {
2507 rc = VERR_NO_STR_MEMORY;
2508 return rc;
2509 }
2510
2511 PRTDIRENTRYEX pPluginDirEntry = NULL;
2512 size_t cbPluginDirEntry = sizeof(RTDIRENTRYEX);
2513 /* The plugins are in the same directory as the other shared libs. */
2514 rc = RTDirOpenFiltered(&pPluginDir, pszPluginFilter, RTDIRFILTER_WINNT);
2515 if (RT_FAILURE(rc))
2516 {
2517 /* On Windows the above immediately signals that there are no
2518 * files matching, while on other platforms enumerating the
2519 * files below fails. Either way: no plugins. */
2520 goto out;
2521 }
2522
2523 pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
2524 if (!pPluginDirEntry)
2525 {
2526 rc = VERR_NO_MEMORY;
2527 goto out;
2528 }
2529
2530 while ((rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK)) != VERR_NO_MORE_FILES)
2531 {
2532 RTLDRMOD hPlugin = NIL_RTLDRMOD;
2533 PFNVDCACHEFORMATLOAD pfnVDCacheLoad = NULL;
2534 PVDCACHEBACKEND pBackend = NULL;
2535 char *pszPluginPath = NULL;
2536
2537 if (rc == VERR_BUFFER_OVERFLOW)
2538 {
2539 /* allocate new buffer. */
2540 RTMemFree(pPluginDirEntry);
2541 pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbPluginDirEntry);
2542 if (!pPluginDirEntry)
2543 {
2544 rc = VERR_NO_MEMORY;
2545 break;
2546 }
2547 /* Retry. */
2548 rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
2549 if (RT_FAILURE(rc))
2550 break;
2551 }
2552 else if (RT_FAILURE(rc))
2553 break;
2554
2555 /* We got the new entry. */
2556 if (!RTFS_IS_FILE(pPluginDirEntry->Info.Attr.fMode))
2557 continue;
2558
2559 /* Prepend the path to the libraries. */
2560 pszPluginPath = RTPathJoinA(szPath, pPluginDirEntry->szName);
2561 if (!pszPluginPath)
2562 {
2563 rc = VERR_NO_STR_MEMORY;
2564 break;
2565 }
2566
2567 rc = SUPR3HardenedLdrLoadPlugIn(pszPluginPath, &hPlugin, NULL);
2568 if (RT_SUCCESS(rc))
2569 {
2570 rc = RTLdrGetSymbol(hPlugin, VD_CACHEFORMAT_LOAD_NAME, (void**)&pfnVDCacheLoad);
2571 if (RT_FAILURE(rc) || !pfnVDCacheLoad)
2572 {
2573 LogFunc(("error resolving the entry point %s in plugin %s, rc=%Rrc, pfnVDCacheLoad=%#p\n",
2574 VD_CACHEFORMAT_LOAD_NAME, pPluginDirEntry->szName, rc, pfnVDCacheLoad));
2575 if (RT_SUCCESS(rc))
2576 rc = VERR_SYMBOL_NOT_FOUND;
2577 }
2578
2579 if (RT_SUCCESS(rc))
2580 {
2581 /* Get the function table. */
2582 rc = pfnVDCacheLoad(&pBackend);
2583 if (RT_SUCCESS(rc) && pBackend->cbSize == sizeof(VDCACHEBACKEND))
2584 {
2585 pBackend->hPlugin = hPlugin;
2586 vdAddCacheBackend(pBackend);
2587 }
2588 else
2589 LogFunc(("ignored plugin '%s': pBackend->cbSize=%d rc=%Rrc\n", pszPluginPath, pBackend->cbSize, rc));
2590 }
2591 else
2592 LogFunc(("ignored plugin '%s': rc=%Rrc\n", pszPluginPath, rc));
2593
2594 if (RT_FAILURE(rc))
2595 RTLdrClose(hPlugin);
2596 }
2597 RTStrFree(pszPluginPath);
2598 }
2599out:
2600 if (rc == VERR_NO_MORE_FILES)
2601 rc = VINF_SUCCESS;
2602 RTStrFree(pszPluginFilter);
2603 if (pPluginDirEntry)
2604 RTMemFree(pPluginDirEntry);
2605 if (pPluginDir)
2606 RTDirClose(pPluginDir);
2607 return rc;
2608#else
2609 return VINF_SUCCESS;
2610#endif
2611}
2612
2613/**
2614 * VD async I/O interface open callback.
2615 */
2616static int vdIOOpenFallback(void *pvUser, const char *pszLocation,
2617 uint32_t fOpen, PFNVDCOMPLETED pfnCompleted,
2618 void **ppStorage)
2619{
2620 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)RTMemAllocZ(sizeof(VDIIOFALLBACKSTORAGE));
2621
2622 if (!pStorage)
2623 return VERR_NO_MEMORY;
2624
2625 pStorage->pfnCompleted = pfnCompleted;
2626
2627 /* Open the file. */
2628 int rc = RTFileOpen(&pStorage->File, pszLocation, fOpen);
2629 if (RT_SUCCESS(rc))
2630 {
2631 *ppStorage = pStorage;
2632 return VINF_SUCCESS;
2633 }
2634
2635 RTMemFree(pStorage);
2636 return rc;
2637}
2638
2639/**
2640 * VD async I/O interface close callback.
2641 */
2642static int vdIOCloseFallback(void *pvUser, void *pvStorage)
2643{
2644 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
2645
2646 RTFileClose(pStorage->File);
2647 RTMemFree(pStorage);
2648 return VINF_SUCCESS;
2649}
2650
2651static int vdIODeleteFallback(void *pvUser, const char *pcszFilename)
2652{
2653 return RTFileDelete(pcszFilename);
2654}
2655
2656static int vdIOMoveFallback(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove)
2657{
2658 return RTFileMove(pcszSrc, pcszDst, fMove);
2659}
2660
2661static int vdIOGetFreeSpaceFallback(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace)
2662{
2663 return RTFsQuerySizes(pcszFilename, NULL, pcbFreeSpace, NULL, NULL);
2664}
2665
2666static int vdIOGetModificationTimeFallback(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime)
2667{
2668 RTFSOBJINFO info;
2669 int rc = RTPathQueryInfo(pcszFilename, &info, RTFSOBJATTRADD_NOTHING);
2670 if (RT_SUCCESS(rc))
2671 *pModificationTime = info.ModificationTime;
2672 return rc;
2673}
2674
2675/**
2676 * VD async I/O interface callback for retrieving the file size.
2677 */
2678static int vdIOGetSizeFallback(void *pvUser, void *pvStorage, uint64_t *pcbSize)
2679{
2680 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
2681
2682 return RTFileGetSize(pStorage->File, pcbSize);
2683}
2684
2685/**
2686 * VD async I/O interface callback for setting the file size.
2687 */
2688static int vdIOSetSizeFallback(void *pvUser, void *pvStorage, uint64_t cbSize)
2689{
2690 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
2691
2692 return RTFileSetSize(pStorage->File, cbSize);
2693}
2694
2695/**
2696 * VD async I/O interface callback for a synchronous write to the file.
2697 */
2698static int vdIOWriteSyncFallback(void *pvUser, void *pvStorage, uint64_t uOffset,
2699 const void *pvBuf, size_t cbWrite, size_t *pcbWritten)
2700{
2701 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
2702
2703 return RTFileWriteAt(pStorage->File, uOffset, pvBuf, cbWrite, pcbWritten);
2704}
2705
2706/**
2707 * VD async I/O interface callback for a synchronous read from the file.
2708 */
2709static int vdIOReadSyncFallback(void *pvUser, void *pvStorage, uint64_t uOffset,
2710 void *pvBuf, size_t cbRead, size_t *pcbRead)
2711{
2712 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
2713
2714 return RTFileReadAt(pStorage->File, uOffset, pvBuf, cbRead, pcbRead);
2715}
2716
2717/**
2718 * VD async I/O interface callback for a synchronous flush of the file data.
2719 */
2720static int vdIOFlushSyncFallback(void *pvUser, void *pvStorage)
2721{
2722 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
2723
2724 return RTFileFlush(pStorage->File);
2725}
2726
2727/**
2728 * VD async I/O interface callback for a asynchronous read from the file.
2729 */
2730static int vdIOReadAsyncFallback(void *pvUser, void *pStorage, uint64_t uOffset,
2731 PCRTSGSEG paSegments, size_t cSegments,
2732 size_t cbRead, void *pvCompletion,
2733 void **ppTask)
2734{
2735 return VERR_NOT_IMPLEMENTED;
2736}
2737
2738/**
2739 * VD async I/O interface callback for a asynchronous write to the file.
2740 */
2741static int vdIOWriteAsyncFallback(void *pvUser, void *pStorage, uint64_t uOffset,
2742 PCRTSGSEG paSegments, size_t cSegments,
2743 size_t cbWrite, void *pvCompletion,
2744 void **ppTask)
2745{
2746 return VERR_NOT_IMPLEMENTED;
2747}
2748
2749/**
2750 * VD async I/O interface callback for a asynchronous flush of the file data.
2751 */
2752static int vdIOFlushAsyncFallback(void *pvUser, void *pStorage,
2753 void *pvCompletion, void **ppTask)
2754{
2755 return VERR_NOT_IMPLEMENTED;
2756}
2757
2758/**
2759 * Internal - Continues an I/O context after
2760 * it was halted because of an active transfer.
2761 */
2762static int vdIoCtxContinue(PVDIOCTX pIoCtx, int rcReq)
2763{
2764 PVBOXHDD pDisk = pIoCtx->pDisk;
2765 int rc = VINF_SUCCESS;
2766
2767 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
2768
2769 if (RT_FAILURE(rcReq))
2770 ASMAtomicCmpXchgS32(&pIoCtx->rcReq, rcReq, VINF_SUCCESS);
2771
2772 if (!pIoCtx->fBlocked)
2773 {
2774 /* Continue the transfer */
2775 rc = vdIoCtxProcess(pIoCtx);
2776
2777 if ( rc == VINF_VD_ASYNC_IO_FINISHED
2778 && ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
2779 {
2780 LogFlowFunc(("I/O context completed pIoCtx=%#p\n", pIoCtx));
2781 if (pIoCtx->pIoCtxParent)
2782 {
2783 PVDIOCTX pIoCtxParent = pIoCtx->pIoCtxParent;
2784
2785 Assert(!pIoCtxParent->pIoCtxParent);
2786 if (RT_FAILURE(pIoCtx->rcReq))
2787 ASMAtomicCmpXchgS32(&pIoCtxParent->rcReq, pIoCtx->rcReq, VINF_SUCCESS);
2788
2789 if (pIoCtx->enmTxDir == VDIOCTXTXDIR_WRITE)
2790 {
2791 LogFlowFunc(("I/O context transferred %u bytes for the parent pIoCtxParent=%p\n",
2792 pIoCtx->Type.Child.cbTransferParent, pIoCtxParent));
2793
2794 /* Update the parent state. */
2795 Assert(pIoCtxParent->cbTransferLeft >= pIoCtx->Type.Child.cbTransferParent);
2796 ASMAtomicSubU32(&pIoCtxParent->cbTransferLeft, pIoCtx->Type.Child.cbTransferParent);
2797 }
2798 else
2799 Assert(pIoCtx->enmTxDir == VDIOCTXTXDIR_FLUSH);
2800
2801 /*
2802 * A completed child write means that we finished growing the image.
2803 * We have to process any pending writes now.
2804 */
2805 vdIoCtxUnlockDisk(pDisk, pIoCtxParent, false /* fProcessDeferredReqs */);
2806
2807 /* Unblock the parent */
2808 pIoCtxParent->fBlocked = false;
2809
2810 rc = vdIoCtxProcess(pIoCtxParent);
2811
2812 if ( rc == VINF_VD_ASYNC_IO_FINISHED
2813 && ASMAtomicCmpXchgBool(&pIoCtxParent->fComplete, true, false))
2814 {
2815 RTCritSectLeave(&pDisk->CritSect);
2816 LogFlowFunc(("Parent I/O context completed pIoCtxParent=%#p rcReq=%Rrc\n", pIoCtxParent, pIoCtxParent->rcReq));
2817 pIoCtxParent->Type.Root.pfnComplete(pIoCtxParent->Type.Root.pvUser1,
2818 pIoCtxParent->Type.Root.pvUser2,
2819 pIoCtxParent->rcReq);
2820 vdThreadFinishWrite(pDisk);
2821 vdIoCtxFree(pDisk, pIoCtxParent);
2822 RTCritSectEnter(&pDisk->CritSect);
2823 }
2824
2825 /* Process any pending writes if the current request didn't caused another growing. */
2826 if ( !RTListIsEmpty(&pDisk->ListWriteLocked)
2827 && !vdIoCtxIsDiskLockOwner(pDisk, pIoCtx))
2828 {
2829 RTLISTNODE ListTmp;
2830
2831 LogFlowFunc(("Before: pNext=%#p pPrev=%#p\n", pDisk->ListWriteLocked.pNext,
2832 pDisk->ListWriteLocked.pPrev));
2833
2834 RTListMove(&ListTmp, &pDisk->ListWriteLocked);
2835
2836 LogFlowFunc(("After: pNext=%#p pPrev=%#p\n", pDisk->ListWriteLocked.pNext,
2837 pDisk->ListWriteLocked.pPrev));
2838
2839 RTCritSectLeave(&pDisk->CritSect);
2840
2841 /* Process the list. */
2842 do
2843 {
2844 PVDIOCTXDEFERRED pDeferred = RTListGetFirst(&ListTmp, VDIOCTXDEFERRED, NodeDeferred);
2845 PVDIOCTX pIoCtxWait = pDeferred->pIoCtx;
2846
2847 AssertPtr(pIoCtxWait);
2848
2849 RTListNodeRemove(&pDeferred->NodeDeferred);
2850 RTMemFree(pDeferred);
2851
2852 Assert(!pIoCtxWait->pIoCtxParent);
2853
2854 pIoCtxWait->fBlocked = false;
2855 LogFlowFunc(("Processing waiting I/O context pIoCtxWait=%#p\n", pIoCtxWait));
2856
2857 rc = vdIoCtxProcess(pIoCtxWait);
2858 if ( rc == VINF_VD_ASYNC_IO_FINISHED
2859 && ASMAtomicCmpXchgBool(&pIoCtxWait->fComplete, true, false))
2860 {
2861 LogFlowFunc(("Waiting I/O context completed pIoCtxWait=%#p\n", pIoCtxWait));
2862 vdThreadFinishWrite(pDisk);
2863 pIoCtxWait->Type.Root.pfnComplete(pIoCtxWait->Type.Root.pvUser1,
2864 pIoCtxWait->Type.Root.pvUser2,
2865 pIoCtxWait->rcReq);
2866 vdIoCtxFree(pDisk, pIoCtxWait);
2867 }
2868 } while (!RTListIsEmpty(&ListTmp));
2869
2870 RTCritSectEnter(&pDisk->CritSect);
2871 }
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 nImageFromSame=%u nImageToSame=%u uImageFlags=%#x pDstUuid=%#p uOpenFlags=%#x pVDIfsOperation=%#p pDstVDIfsImage=%#p pDstVDIfsOperation=%#p\n",
6019 pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename, cbSize, nImageFromSame, nImageToSame, 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 != VD_IMAGE_CONTENT_UNKNOWN));
6218 unsigned cImagesFromReadBack, cImagesToReadBack;
6219
6220 if (nImageFromSame == VD_IMAGE_CONTENT_UNKNOWN)
6221 cImagesFromReadBack = 0;
6222 else
6223 {
6224 if (nImage == VD_LAST_IMAGE)
6225 cImagesFromReadBack = pDiskFrom->cImages - nImageFromSame - 1;
6226 else
6227 cImagesFromReadBack = nImage - nImageFromSame;
6228 }
6229
6230 if (nImageToSame == VD_IMAGE_CONTENT_UNKNOWN)
6231 cImagesToReadBack = 0;
6232 else
6233 cImagesToReadBack = pDiskTo->cImages - nImageToSame - 1;
6234
6235 /* Copy the data. */
6236 rc = vdCopyHelper(pDiskFrom, pImageFrom, pDiskTo, cbSize,
6237 cImagesFromReadBack, cImagesToReadBack,
6238 fSuppressRedundantIo, pIfProgress, pCbProgress,
6239 pDstIfProgress, pDstCbProgress);
6240
6241 if (RT_SUCCESS(rc))
6242 {
6243 rc2 = vdThreadStartWrite(pDiskTo);
6244 AssertRC(rc2);
6245 fLockWriteTo = true;
6246
6247 /* Only set modification UUID if it is non-null, since the source
6248 * backend might not provide a valid modification UUID. */
6249 if (!RTUuidIsNull(&ImageModificationUuid))
6250 pImageTo->Backend->pfnSetModificationUuid(pImageTo->pBackendData, &ImageModificationUuid);
6251
6252 /* Set the requested open flags if they differ from the value
6253 * required for creating the image and copying the contents. */
6254 if ( pImageTo && pszFilename
6255 && uOpenFlags != (uOpenFlags & ~VD_OPEN_FLAGS_READONLY))
6256 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
6257 uOpenFlags);
6258 }
6259 } while (0);
6260
6261 if (RT_FAILURE(rc) && pImageTo && pszFilename)
6262 {
6263 /* Take the write lock only if it is not taken. Not worth making the
6264 * above code even more complicated. */
6265 if (RT_UNLIKELY(!fLockWriteTo))
6266 {
6267 rc2 = vdThreadStartWrite(pDiskTo);
6268 AssertRC(rc2);
6269 fLockWriteTo = true;
6270 }
6271 /* Error detected, but new image created. Remove image from list. */
6272 vdRemoveImageFromList(pDiskTo, pImageTo);
6273
6274 /* Close and delete image. */
6275 rc2 = pImageTo->Backend->pfnClose(pImageTo->pBackendData, true);
6276 AssertRC(rc2);
6277 pImageTo->pBackendData = NULL;
6278
6279 /* Free remaining resources. */
6280 if (pImageTo->pszFilename)
6281 RTStrFree(pImageTo->pszFilename);
6282
6283 RTMemFree(pImageTo);
6284 }
6285
6286 if (RT_UNLIKELY(fLockWriteTo))
6287 {
6288 rc2 = vdThreadFinishWrite(pDiskTo);
6289 AssertRC(rc2);
6290 }
6291 if (RT_UNLIKELY(fLockWriteFrom))
6292 {
6293 rc2 = vdThreadFinishWrite(pDiskFrom);
6294 AssertRC(rc2);
6295 }
6296 else if (RT_UNLIKELY(fLockReadFrom))
6297 {
6298 rc2 = vdThreadFinishRead(pDiskFrom);
6299 AssertRC(rc2);
6300 }
6301
6302 if (RT_SUCCESS(rc))
6303 {
6304 if (pCbProgress && pCbProgress->pfnProgress)
6305 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
6306 if (pDstCbProgress && pDstCbProgress->pfnProgress)
6307 pDstCbProgress->pfnProgress(pDstIfProgress->pvUser, 100);
6308 }
6309
6310 LogFlowFunc(("returns %Rrc\n", rc));
6311 return rc;
6312}
6313
6314/**
6315 * Copies an image from one HDD container to another.
6316 * The copy is opened in the target HDD container.
6317 * It is possible to convert between different image formats, because the
6318 * backend for the destination may be different from the source.
6319 * If both the source and destination reference the same HDD container,
6320 * then the image is moved (by copying/deleting or renaming) to the new location.
6321 * The source container is unchanged if the move operation fails, otherwise
6322 * the image at the new location is opened in the same way as the old one was.
6323 *
6324 * @returns VBox status code.
6325 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
6326 * @param pDiskFrom Pointer to source HDD container.
6327 * @param nImage Image number, counts from 0. 0 is always base image of container.
6328 * @param pDiskTo Pointer to destination HDD container.
6329 * @param pszBackend Name of the image file backend to use.
6330 * @param pszFilename New name of the image (may be NULL if pDiskFrom == pDiskTo).
6331 * @param fMoveByRename If true, attempt to perform a move by renaming (if successful the new size is ignored).
6332 * @param cbSize New image size (0 means leave unchanged).
6333 * @param uImageFlags Flags specifying special destination image features.
6334 * @param pDstUuid New UUID of the destination image. If NULL, a new UUID is created.
6335 * This parameter is used if and only if a true copy is created.
6336 * In all rename/move cases the UUIDs are copied over.
6337 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
6338 * Only used if the destination image is created.
6339 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
6340 * @param pDstVDIfsImage Pointer to the per-image VD interface list, for the
6341 * destination image.
6342 * @param pDstVDIfsOperation Pointer to the per-image VD interface list,
6343 * for the destination image.
6344 */
6345VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
6346 const char *pszBackend, const char *pszFilename,
6347 bool fMoveByRename, uint64_t cbSize,
6348 unsigned uImageFlags, PCRTUUID pDstUuid,
6349 unsigned uOpenFlags, PVDINTERFACE pVDIfsOperation,
6350 PVDINTERFACE pDstVDIfsImage,
6351 PVDINTERFACE pDstVDIfsOperation)
6352{
6353 return VDCopyEx(pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename,
6354 cbSize, VD_IMAGE_CONTENT_UNKNOWN, VD_IMAGE_CONTENT_UNKNOWN,
6355 uImageFlags, pDstUuid, uOpenFlags, pVDIfsOperation,
6356 pDstVDIfsImage, pDstVDIfsOperation);
6357}
6358
6359/**
6360 * Optimizes the storage consumption of an image. Typically the unused blocks
6361 * have to be wiped with zeroes to achieve a substantial reduced storage use.
6362 * Another optimization done is reordering the image blocks, which can provide
6363 * a significant performance boost, as reads and writes tend to use less random
6364 * file offsets.
6365 *
6366 * @return VBox status code.
6367 * @return VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
6368 * @return VERR_VD_IMAGE_READ_ONLY if image is not writable.
6369 * @return VERR_NOT_SUPPORTED if this kind of image can be compacted, but
6370 * the code for this isn't implemented yet.
6371 * @param pDisk Pointer to HDD container.
6372 * @param nImage Image number, counts from 0. 0 is always base image of container.
6373 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
6374 */
6375VBOXDDU_DECL(int) VDCompact(PVBOXHDD pDisk, unsigned nImage,
6376 PVDINTERFACE pVDIfsOperation)
6377{
6378 int rc = VINF_SUCCESS;
6379 int rc2;
6380 bool fLockRead = false, fLockWrite = false;
6381 void *pvBuf = NULL;
6382 void *pvTmp = NULL;
6383
6384 LogFlowFunc(("pDisk=%#p nImage=%u pVDIfsOperation=%#p\n",
6385 pDisk, nImage, pVDIfsOperation));
6386
6387 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
6388 VDINTERFACETYPE_PROGRESS);
6389 PVDINTERFACEPROGRESS pCbProgress = NULL;
6390 if (pIfProgress)
6391 pCbProgress = VDGetInterfaceProgress(pIfProgress);
6392
6393 do {
6394 /* Check arguments. */
6395 AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
6396 rc = VERR_INVALID_PARAMETER);
6397 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE,
6398 ("u32Signature=%08x\n", pDisk->u32Signature));
6399
6400 rc2 = vdThreadStartRead(pDisk);
6401 AssertRC(rc2);
6402 fLockRead = true;
6403
6404 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6405 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
6406
6407 /* If there is no compact callback for not file based backends then
6408 * the backend doesn't need compaction. No need to make much fuss about
6409 * this. For file based ones signal this as not yet supported. */
6410 if (!pImage->Backend->pfnCompact)
6411 {
6412 if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
6413 rc = VERR_NOT_SUPPORTED;
6414 else
6415 rc = VINF_SUCCESS;
6416 break;
6417 }
6418
6419 /* Insert interface for reading parent state into per-operation list,
6420 * if there is a parent image. */
6421 VDINTERFACE IfOpParent;
6422 VDINTERFACEPARENTSTATE ParentCb;
6423 VDPARENTSTATEDESC ParentUser;
6424 if (pImage->pPrev)
6425 {
6426 ParentCb.cbSize = sizeof(ParentCb);
6427 ParentCb.enmInterface = VDINTERFACETYPE_PARENTSTATE;
6428 ParentCb.pfnParentRead = vdParentRead;
6429 ParentUser.pDisk = pDisk;
6430 ParentUser.pImage = pImage->pPrev;
6431 rc = VDInterfaceAdd(&IfOpParent, "VDCompact_ParentState", VDINTERFACETYPE_PARENTSTATE,
6432 &ParentCb, &ParentUser, &pVDIfsOperation);
6433 AssertRC(rc);
6434 }
6435
6436 rc2 = vdThreadFinishRead(pDisk);
6437 AssertRC(rc2);
6438 fLockRead = false;
6439
6440 rc2 = vdThreadStartWrite(pDisk);
6441 AssertRC(rc2);
6442 fLockWrite = true;
6443
6444 rc = pImage->Backend->pfnCompact(pImage->pBackendData,
6445 0, 99,
6446 pDisk->pVDIfsDisk,
6447 pImage->pVDIfsImage,
6448 pVDIfsOperation);
6449 } while (0);
6450
6451 if (RT_UNLIKELY(fLockWrite))
6452 {
6453 rc2 = vdThreadFinishWrite(pDisk);
6454 AssertRC(rc2);
6455 }
6456 else if (RT_UNLIKELY(fLockRead))
6457 {
6458 rc2 = vdThreadFinishRead(pDisk);
6459 AssertRC(rc2);
6460 }
6461
6462 if (pvBuf)
6463 RTMemTmpFree(pvBuf);
6464 if (pvTmp)
6465 RTMemTmpFree(pvTmp);
6466
6467 if (RT_SUCCESS(rc))
6468 {
6469 if (pCbProgress && pCbProgress->pfnProgress)
6470 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
6471 }
6472
6473 LogFlowFunc(("returns %Rrc\n", rc));
6474 return rc;
6475}
6476
6477/**
6478 * Resizes the the given disk image to the given size.
6479 *
6480 * @return VBox status
6481 * @return VERR_VD_IMAGE_READ_ONLY if image is not writable.
6482 * @return VERR_NOT_SUPPORTED if this kind of image can be compacted, but
6483 *
6484 * @param pDisk Pointer to the HDD container.
6485 * @param cbSize New size of the image.
6486 * @param pPCHSGeometry Pointer to the new physical disk geometry <= (16383,16,63). Not NULL.
6487 * @param pLCHSGeometry Pointer to the new logical disk geometry <= (x,255,63). Not NULL.
6488 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
6489 */
6490VBOXDDU_DECL(int) VDResize(PVBOXHDD pDisk, uint64_t cbSize,
6491 PCVDGEOMETRY pPCHSGeometry,
6492 PCVDGEOMETRY pLCHSGeometry,
6493 PVDINTERFACE pVDIfsOperation)
6494{
6495 /** @todo r=klaus resizing was designed to be part of VDCopy, so having a separate function is not desirable. */
6496 int rc = VINF_SUCCESS;
6497 int rc2;
6498 bool fLockRead = false, fLockWrite = false;
6499
6500 LogFlowFunc(("pDisk=%#p cbSize=%llu pVDIfsOperation=%#p\n",
6501 pDisk, cbSize, pVDIfsOperation));
6502
6503 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
6504 VDINTERFACETYPE_PROGRESS);
6505 PVDINTERFACEPROGRESS pCbProgress = NULL;
6506 if (pIfProgress)
6507 pCbProgress = VDGetInterfaceProgress(pIfProgress);
6508
6509 do {
6510 /* Check arguments. */
6511 AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
6512 rc = VERR_INVALID_PARAMETER);
6513 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE,
6514 ("u32Signature=%08x\n", pDisk->u32Signature));
6515
6516 rc2 = vdThreadStartRead(pDisk);
6517 AssertRC(rc2);
6518 fLockRead = true;
6519
6520 /* Not supported if the disk has child images attached. */
6521 AssertMsgBreakStmt(pDisk->cImages == 1, ("cImages=%u\n", pDisk->cImages),
6522 rc = VERR_NOT_SUPPORTED);
6523
6524 PVDIMAGE pImage = pDisk->pBase;
6525
6526 /* If there is no compact callback for not file based backends then
6527 * the backend doesn't need compaction. No need to make much fuss about
6528 * this. For file based ones signal this as not yet supported. */
6529 if (!pImage->Backend->pfnResize)
6530 {
6531 if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
6532 rc = VERR_NOT_SUPPORTED;
6533 else
6534 rc = VINF_SUCCESS;
6535 break;
6536 }
6537
6538 rc2 = vdThreadFinishRead(pDisk);
6539 AssertRC(rc2);
6540 fLockRead = false;
6541
6542 rc2 = vdThreadStartWrite(pDisk);
6543 AssertRC(rc2);
6544 fLockWrite = true;
6545
6546 VDGEOMETRY PCHSGeometryOld;
6547 VDGEOMETRY LCHSGeometryOld;
6548 PCVDGEOMETRY pPCHSGeometryNew;
6549 PCVDGEOMETRY pLCHSGeometryNew;
6550
6551 if (pPCHSGeometry->cCylinders == 0)
6552 {
6553 /* Auto-detect marker, calculate new value ourself. */
6554 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData, &PCHSGeometryOld);
6555 if (RT_SUCCESS(rc) && (PCHSGeometryOld.cCylinders != 0))
6556 PCHSGeometryOld.cCylinders = RT_MIN(cbSize / 512 / PCHSGeometryOld.cHeads / PCHSGeometryOld.cSectors, 16383);
6557 else if (rc == VERR_VD_GEOMETRY_NOT_SET)
6558 rc = VINF_SUCCESS;
6559
6560 pPCHSGeometryNew = &PCHSGeometryOld;
6561 }
6562 else
6563 pPCHSGeometryNew = pPCHSGeometry;
6564
6565 if (pLCHSGeometry->cCylinders == 0)
6566 {
6567 /* Auto-detect marker, calculate new value ourself. */
6568 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData, &LCHSGeometryOld);
6569 if (RT_SUCCESS(rc) && (LCHSGeometryOld.cCylinders != 0))
6570 LCHSGeometryOld.cCylinders = cbSize / 512 / LCHSGeometryOld.cHeads / LCHSGeometryOld.cSectors;
6571 else if (rc == VERR_VD_GEOMETRY_NOT_SET)
6572 rc = VINF_SUCCESS;
6573
6574 pLCHSGeometryNew = &LCHSGeometryOld;
6575 }
6576 else
6577 pLCHSGeometryNew = pLCHSGeometry;
6578
6579 if (RT_SUCCESS(rc))
6580 rc = pImage->Backend->pfnResize(pImage->pBackendData,
6581 cbSize,
6582 pPCHSGeometryNew,
6583 pLCHSGeometryNew,
6584 0, 99,
6585 pDisk->pVDIfsDisk,
6586 pImage->pVDIfsImage,
6587 pVDIfsOperation);
6588 } while (0);
6589
6590 if (RT_UNLIKELY(fLockWrite))
6591 {
6592 rc2 = vdThreadFinishWrite(pDisk);
6593 AssertRC(rc2);
6594 }
6595 else if (RT_UNLIKELY(fLockRead))
6596 {
6597 rc2 = vdThreadFinishRead(pDisk);
6598 AssertRC(rc2);
6599 }
6600
6601 if (RT_SUCCESS(rc))
6602 {
6603 if (pCbProgress && pCbProgress->pfnProgress)
6604 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
6605 }
6606
6607 LogFlowFunc(("returns %Rrc\n", rc));
6608 return rc;
6609}
6610
6611/**
6612 * Closes the last opened image file in HDD container.
6613 * If previous image file was opened in read-only mode (the normal case) and
6614 * the last opened image is in read-write mode then the previous image will be
6615 * reopened in read/write mode.
6616 *
6617 * @returns VBox status code.
6618 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
6619 * @param pDisk Pointer to HDD container.
6620 * @param fDelete If true, delete the image from the host disk.
6621 */
6622VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete)
6623{
6624 int rc = VINF_SUCCESS;
6625 int rc2;
6626 bool fLockWrite = false;
6627
6628 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
6629 do
6630 {
6631 /* sanity check */
6632 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6633 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6634
6635 /* Not worth splitting this up into a read lock phase and write
6636 * lock phase, as closing an image is a relatively fast operation
6637 * dominated by the part which needs the write lock. */
6638 rc2 = vdThreadStartWrite(pDisk);
6639 AssertRC(rc2);
6640 fLockWrite = true;
6641
6642 PVDIMAGE pImage = pDisk->pLast;
6643 if (!pImage)
6644 {
6645 rc = VERR_VD_NOT_OPENED;
6646 break;
6647 }
6648 unsigned uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
6649 /* Remove image from list of opened images. */
6650 vdRemoveImageFromList(pDisk, pImage);
6651 /* Close (and optionally delete) image. */
6652 rc = pImage->Backend->pfnClose(pImage->pBackendData, fDelete);
6653 /* Free remaining resources related to the image. */
6654 RTStrFree(pImage->pszFilename);
6655 RTMemFree(pImage);
6656
6657 pImage = pDisk->pLast;
6658 if (!pImage)
6659 break;
6660
6661 /* If disk was previously in read/write mode, make sure it will stay
6662 * like this (if possible) after closing this image. Set the open flags
6663 * accordingly. */
6664 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
6665 {
6666 uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
6667 uOpenFlags &= ~ VD_OPEN_FLAGS_READONLY;
6668 rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData, uOpenFlags);
6669 }
6670
6671 /* Cache disk information. */
6672 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
6673
6674 /* Cache PCHS geometry. */
6675 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
6676 &pDisk->PCHSGeometry);
6677 if (RT_FAILURE(rc2))
6678 {
6679 pDisk->PCHSGeometry.cCylinders = 0;
6680 pDisk->PCHSGeometry.cHeads = 0;
6681 pDisk->PCHSGeometry.cSectors = 0;
6682 }
6683 else
6684 {
6685 /* Make sure the PCHS geometry is properly clipped. */
6686 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
6687 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
6688 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
6689 }
6690
6691 /* Cache LCHS geometry. */
6692 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
6693 &pDisk->LCHSGeometry);
6694 if (RT_FAILURE(rc2))
6695 {
6696 pDisk->LCHSGeometry.cCylinders = 0;
6697 pDisk->LCHSGeometry.cHeads = 0;
6698 pDisk->LCHSGeometry.cSectors = 0;
6699 }
6700 else
6701 {
6702 /* Make sure the LCHS geometry is properly clipped. */
6703 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
6704 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
6705 }
6706 } while (0);
6707
6708 if (RT_UNLIKELY(fLockWrite))
6709 {
6710 rc2 = vdThreadFinishWrite(pDisk);
6711 AssertRC(rc2);
6712 }
6713
6714 LogFlowFunc(("returns %Rrc\n", rc));
6715 return rc;
6716}
6717
6718/**
6719 * Closes the currently opened cache image file in HDD container.
6720 *
6721 * @return VBox status code.
6722 * @return VERR_VD_NOT_OPENED if no cache is opened in HDD container.
6723 * @param pDisk Pointer to HDD container.
6724 * @param fDelete If true, delete the image from the host disk.
6725 */
6726VBOXDDU_DECL(int) VDCacheClose(PVBOXHDD pDisk, bool fDelete)
6727{
6728 int rc = VINF_SUCCESS;
6729 int rc2;
6730 bool fLockWrite = false;
6731 PVDCACHE pCache = NULL;
6732
6733 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
6734
6735 do
6736 {
6737 /* sanity check */
6738 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6739 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6740
6741 rc2 = vdThreadStartWrite(pDisk);
6742 AssertRC(rc2);
6743 fLockWrite = true;
6744
6745 AssertPtrBreakStmt(pDisk->pCache, rc = VERR_VD_CACHE_NOT_FOUND);
6746
6747 pCache = pDisk->pCache;
6748 pDisk->pCache = NULL;
6749
6750 pCache->Backend->pfnClose(pCache->pBackendData, fDelete);
6751 if (pCache->pszFilename)
6752 RTStrFree(pCache->pszFilename);
6753 RTMemFree(pCache);
6754 } while (0);
6755
6756 if (RT_LIKELY(fLockWrite))
6757 {
6758 rc2 = vdThreadFinishWrite(pDisk);
6759 AssertRC(rc2);
6760 }
6761
6762 LogFlowFunc(("returns %Rrc\n", rc));
6763 return rc;
6764}
6765
6766/**
6767 * Closes all opened image files in HDD container.
6768 *
6769 * @returns VBox status code.
6770 * @param pDisk Pointer to HDD container.
6771 */
6772VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk)
6773{
6774 int rc = VINF_SUCCESS;
6775 int rc2;
6776 bool fLockWrite = false;
6777
6778 LogFlowFunc(("pDisk=%#p\n", pDisk));
6779 do
6780 {
6781 /* sanity check */
6782 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6783 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6784
6785 /* Lock the entire operation. */
6786 rc2 = vdThreadStartWrite(pDisk);
6787 AssertRC(rc2);
6788 fLockWrite = true;
6789
6790 PVDCACHE pCache = pDisk->pCache;
6791 if (pCache)
6792 {
6793 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, false);
6794 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
6795 rc = rc2;
6796
6797 if (pCache->pszFilename)
6798 RTStrFree(pCache->pszFilename);
6799 RTMemFree(pCache);
6800 }
6801
6802 PVDIMAGE pImage = pDisk->pLast;
6803 while (VALID_PTR(pImage))
6804 {
6805 PVDIMAGE pPrev = pImage->pPrev;
6806 /* Remove image from list of opened images. */
6807 vdRemoveImageFromList(pDisk, pImage);
6808 /* Close image. */
6809 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, false);
6810 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
6811 rc = rc2;
6812 /* Free remaining resources related to the image. */
6813 RTStrFree(pImage->pszFilename);
6814 RTMemFree(pImage);
6815 pImage = pPrev;
6816 }
6817 Assert(!VALID_PTR(pDisk->pLast));
6818 } while (0);
6819
6820 if (RT_UNLIKELY(fLockWrite))
6821 {
6822 rc2 = vdThreadFinishWrite(pDisk);
6823 AssertRC(rc2);
6824 }
6825
6826 LogFlowFunc(("returns %Rrc\n", rc));
6827 return rc;
6828}
6829
6830/**
6831 * Read data from virtual HDD.
6832 *
6833 * @returns VBox status code.
6834 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
6835 * @param pDisk Pointer to HDD container.
6836 * @param uOffset Offset of first reading byte from start of disk.
6837 * @param pvBuf Pointer to buffer for reading data.
6838 * @param cbRead Number of bytes to read.
6839 */
6840VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuf,
6841 size_t cbRead)
6842{
6843 int rc = VINF_SUCCESS;
6844 int rc2;
6845 bool fLockRead = false;
6846
6847 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbRead=%zu\n",
6848 pDisk, uOffset, pvBuf, cbRead));
6849 do
6850 {
6851 /* sanity check */
6852 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6853 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6854
6855 /* Check arguments. */
6856 AssertMsgBreakStmt(VALID_PTR(pvBuf),
6857 ("pvBuf=%#p\n", pvBuf),
6858 rc = VERR_INVALID_PARAMETER);
6859 AssertMsgBreakStmt(cbRead,
6860 ("cbRead=%zu\n", cbRead),
6861 rc = VERR_INVALID_PARAMETER);
6862
6863 rc2 = vdThreadStartRead(pDisk);
6864 AssertRC(rc2);
6865 fLockRead = true;
6866
6867 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
6868 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
6869 uOffset, cbRead, pDisk->cbSize),
6870 rc = VERR_INVALID_PARAMETER);
6871
6872 PVDIMAGE pImage = pDisk->pLast;
6873 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
6874
6875 rc = vdReadHelper(pDisk, pImage, uOffset, pvBuf, cbRead,
6876 true /* fUpdateCache */);
6877 } while (0);
6878
6879 if (RT_UNLIKELY(fLockRead))
6880 {
6881 rc2 = vdThreadFinishRead(pDisk);
6882 AssertRC(rc2);
6883 }
6884
6885 LogFlowFunc(("returns %Rrc\n", rc));
6886 return rc;
6887}
6888
6889/**
6890 * Write data to virtual HDD.
6891 *
6892 * @returns VBox status code.
6893 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
6894 * @param pDisk Pointer to HDD container.
6895 * @param uOffset Offset of the first byte being
6896 * written from start of disk.
6897 * @param pvBuf Pointer to buffer for writing data.
6898 * @param cbWrite Number of bytes to write.
6899 */
6900VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf,
6901 size_t cbWrite)
6902{
6903 int rc = VINF_SUCCESS;
6904 int rc2;
6905 bool fLockWrite = false;
6906
6907 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbWrite=%zu\n",
6908 pDisk, uOffset, pvBuf, cbWrite));
6909 do
6910 {
6911 /* sanity check */
6912 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6913 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6914
6915 /* Check arguments. */
6916 AssertMsgBreakStmt(VALID_PTR(pvBuf),
6917 ("pvBuf=%#p\n", pvBuf),
6918 rc = VERR_INVALID_PARAMETER);
6919 AssertMsgBreakStmt(cbWrite,
6920 ("cbWrite=%zu\n", cbWrite),
6921 rc = VERR_INVALID_PARAMETER);
6922
6923 rc2 = vdThreadStartWrite(pDisk);
6924 AssertRC(rc2);
6925 fLockWrite = true;
6926
6927 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
6928 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
6929 uOffset, cbWrite, pDisk->cbSize),
6930 rc = VERR_INVALID_PARAMETER);
6931
6932 PVDIMAGE pImage = pDisk->pLast;
6933 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
6934
6935 vdSetModifiedFlag(pDisk);
6936 rc = vdWriteHelper(pDisk, pImage, uOffset, pvBuf, cbWrite,
6937 true /* fUpdateCache */);
6938 if (RT_FAILURE(rc))
6939 break;
6940
6941 /* If there is a merge (in the direction towards a parent) running
6942 * concurrently then we have to also "relay" the write to this parent,
6943 * as the merge position might be already past the position where
6944 * this write is going. The "context" of the write can come from the
6945 * natural chain, since merging either already did or will take care
6946 * of the "other" content which is might be needed to fill the block
6947 * to a full allocation size. The cache doesn't need to be touched
6948 * as this write is covered by the previous one. */
6949 if (RT_UNLIKELY(pDisk->pImageRelay))
6950 rc = vdWriteHelper(pDisk, pDisk->pImageRelay, uOffset,
6951 pvBuf, cbWrite, false /* fUpdateCache */);
6952 } while (0);
6953
6954 if (RT_UNLIKELY(fLockWrite))
6955 {
6956 rc2 = vdThreadFinishWrite(pDisk);
6957 AssertRC(rc2);
6958 }
6959
6960 LogFlowFunc(("returns %Rrc\n", rc));
6961 return rc;
6962}
6963
6964/**
6965 * Make sure the on disk representation of a virtual HDD is up to date.
6966 *
6967 * @returns VBox status code.
6968 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
6969 * @param pDisk Pointer to HDD container.
6970 */
6971VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
6972{
6973 int rc = VINF_SUCCESS;
6974 int rc2;
6975 bool fLockWrite = false;
6976
6977 LogFlowFunc(("pDisk=%#p\n", pDisk));
6978 do
6979 {
6980 /* sanity check */
6981 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6982 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6983
6984 rc2 = vdThreadStartWrite(pDisk);
6985 AssertRC(rc2);
6986 fLockWrite = true;
6987
6988 PVDIMAGE pImage = pDisk->pLast;
6989 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
6990
6991 vdResetModifiedFlag(pDisk);
6992 rc = pImage->Backend->pfnFlush(pImage->pBackendData);
6993
6994 if ( RT_SUCCESS(rc)
6995 && pDisk->pCache)
6996 rc = pDisk->pCache->Backend->pfnFlush(pDisk->pCache->pBackendData);
6997 } while (0);
6998
6999 if (RT_UNLIKELY(fLockWrite))
7000 {
7001 rc2 = vdThreadFinishWrite(pDisk);
7002 AssertRC(rc2);
7003 }
7004
7005 LogFlowFunc(("returns %Rrc\n", rc));
7006 return rc;
7007}
7008
7009/**
7010 * Get number of opened images in HDD container.
7011 *
7012 * @returns Number of opened images for HDD container. 0 if no images have been opened.
7013 * @param pDisk Pointer to HDD container.
7014 */
7015VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk)
7016{
7017 unsigned cImages;
7018 int rc2;
7019 bool fLockRead = false;
7020
7021 LogFlowFunc(("pDisk=%#p\n", pDisk));
7022 do
7023 {
7024 /* sanity check */
7025 AssertPtrBreakStmt(pDisk, cImages = 0);
7026 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7027
7028 rc2 = vdThreadStartRead(pDisk);
7029 AssertRC(rc2);
7030 fLockRead = true;
7031
7032 cImages = pDisk->cImages;
7033 } while (0);
7034
7035 if (RT_UNLIKELY(fLockRead))
7036 {
7037 rc2 = vdThreadFinishRead(pDisk);
7038 AssertRC(rc2);
7039 }
7040
7041 LogFlowFunc(("returns %u\n", cImages));
7042 return cImages;
7043}
7044
7045/**
7046 * Get read/write mode of HDD container.
7047 *
7048 * @returns Virtual disk ReadOnly status.
7049 * @returns true if no image is opened in HDD container.
7050 * @param pDisk Pointer to HDD container.
7051 */
7052VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
7053{
7054 bool fReadOnly;
7055 int rc2;
7056 bool fLockRead = false;
7057
7058 LogFlowFunc(("pDisk=%#p\n", pDisk));
7059 do
7060 {
7061 /* sanity check */
7062 AssertPtrBreakStmt(pDisk, fReadOnly = false);
7063 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7064
7065 rc2 = vdThreadStartRead(pDisk);
7066 AssertRC(rc2);
7067 fLockRead = true;
7068
7069 PVDIMAGE pImage = pDisk->pLast;
7070 AssertPtrBreakStmt(pImage, fReadOnly = true);
7071
7072 unsigned uOpenFlags;
7073 uOpenFlags = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
7074 fReadOnly = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
7075 } while (0);
7076
7077 if (RT_UNLIKELY(fLockRead))
7078 {
7079 rc2 = vdThreadFinishRead(pDisk);
7080 AssertRC(rc2);
7081 }
7082
7083 LogFlowFunc(("returns %d\n", fReadOnly));
7084 return fReadOnly;
7085}
7086
7087/**
7088 * Get total capacity of an image in HDD container.
7089 *
7090 * @returns Virtual disk size in bytes.
7091 * @returns 0 if no image with specified number was not opened.
7092 * @param pDisk Pointer to HDD container.
7093 * @param nImage Image number, counts from 0. 0 is always base image of container.
7094 */
7095VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk, unsigned nImage)
7096{
7097 uint64_t cbSize;
7098 int rc2;
7099 bool fLockRead = false;
7100
7101 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
7102 do
7103 {
7104 /* sanity check */
7105 AssertPtrBreakStmt(pDisk, cbSize = 0);
7106 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7107
7108 rc2 = vdThreadStartRead(pDisk);
7109 AssertRC(rc2);
7110 fLockRead = true;
7111
7112 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7113 AssertPtrBreakStmt(pImage, cbSize = 0);
7114 cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
7115 } while (0);
7116
7117 if (RT_UNLIKELY(fLockRead))
7118 {
7119 rc2 = vdThreadFinishRead(pDisk);
7120 AssertRC(rc2);
7121 }
7122
7123 LogFlowFunc(("returns %llu\n", cbSize));
7124 return cbSize;
7125}
7126
7127/**
7128 * Get total file size of an image in HDD container.
7129 *
7130 * @returns Virtual disk size in bytes.
7131 * @returns 0 if no image is opened in HDD container.
7132 * @param pDisk Pointer to HDD container.
7133 * @param nImage Image number, counts from 0. 0 is always base image of container.
7134 */
7135VBOXDDU_DECL(uint64_t) VDGetFileSize(PVBOXHDD pDisk, unsigned nImage)
7136{
7137 uint64_t cbSize;
7138 int rc2;
7139 bool fLockRead = false;
7140
7141 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
7142 do
7143 {
7144 /* sanity check */
7145 AssertPtrBreakStmt(pDisk, cbSize = 0);
7146 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7147
7148 rc2 = vdThreadStartRead(pDisk);
7149 AssertRC(rc2);
7150 fLockRead = true;
7151
7152 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7153 AssertPtrBreakStmt(pImage, cbSize = 0);
7154 cbSize = pImage->Backend->pfnGetFileSize(pImage->pBackendData);
7155 } while (0);
7156
7157 if (RT_UNLIKELY(fLockRead))
7158 {
7159 rc2 = vdThreadFinishRead(pDisk);
7160 AssertRC(rc2);
7161 }
7162
7163 LogFlowFunc(("returns %llu\n", cbSize));
7164 return cbSize;
7165}
7166
7167/**
7168 * Get virtual disk PCHS geometry stored in HDD container.
7169 *
7170 * @returns VBox status code.
7171 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7172 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
7173 * @param pDisk Pointer to HDD container.
7174 * @param nImage Image number, counts from 0. 0 is always base image of container.
7175 * @param pPCHSGeometry Where to store PCHS geometry. Not NULL.
7176 */
7177VBOXDDU_DECL(int) VDGetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
7178 PVDGEOMETRY pPCHSGeometry)
7179{
7180 int rc = VINF_SUCCESS;
7181 int rc2;
7182 bool fLockRead = false;
7183
7184 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p\n",
7185 pDisk, nImage, pPCHSGeometry));
7186 do
7187 {
7188 /* sanity check */
7189 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7190 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7191
7192 /* Check arguments. */
7193 AssertMsgBreakStmt(VALID_PTR(pPCHSGeometry),
7194 ("pPCHSGeometry=%#p\n", pPCHSGeometry),
7195 rc = VERR_INVALID_PARAMETER);
7196
7197 rc2 = vdThreadStartRead(pDisk);
7198 AssertRC(rc2);
7199 fLockRead = true;
7200
7201 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7202 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7203
7204 if (pImage == pDisk->pLast)
7205 {
7206 /* Use cached information if possible. */
7207 if (pDisk->PCHSGeometry.cCylinders != 0)
7208 *pPCHSGeometry = pDisk->PCHSGeometry;
7209 else
7210 rc = VERR_VD_GEOMETRY_NOT_SET;
7211 }
7212 else
7213 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
7214 pPCHSGeometry);
7215 } while (0);
7216
7217 if (RT_UNLIKELY(fLockRead))
7218 {
7219 rc2 = vdThreadFinishRead(pDisk);
7220 AssertRC(rc2);
7221 }
7222
7223 LogFlowFunc(("%Rrc (PCHS=%u/%u/%u)\n", rc,
7224 pDisk->PCHSGeometry.cCylinders, pDisk->PCHSGeometry.cHeads,
7225 pDisk->PCHSGeometry.cSectors));
7226 return rc;
7227}
7228
7229/**
7230 * Store virtual disk PCHS geometry in HDD container.
7231 *
7232 * Note that in case of unrecoverable error all images in HDD container will be closed.
7233 *
7234 * @returns VBox status code.
7235 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7236 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
7237 * @param pDisk Pointer to HDD container.
7238 * @param nImage Image number, counts from 0. 0 is always base image of container.
7239 * @param pPCHSGeometry Where to load PCHS geometry from. Not NULL.
7240 */
7241VBOXDDU_DECL(int) VDSetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
7242 PCVDGEOMETRY pPCHSGeometry)
7243{
7244 int rc = VINF_SUCCESS;
7245 int rc2;
7246 bool fLockWrite = false;
7247
7248 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
7249 pDisk, nImage, pPCHSGeometry, pPCHSGeometry->cCylinders,
7250 pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
7251 do
7252 {
7253 /* sanity check */
7254 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7255 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7256
7257 /* Check arguments. */
7258 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
7259 && pPCHSGeometry->cHeads <= 16
7260 && pPCHSGeometry->cSectors <= 63,
7261 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
7262 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
7263 pPCHSGeometry->cSectors),
7264 rc = VERR_INVALID_PARAMETER);
7265
7266 rc2 = vdThreadStartWrite(pDisk);
7267 AssertRC(rc2);
7268 fLockWrite = true;
7269
7270 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7271 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7272
7273 if (pImage == pDisk->pLast)
7274 {
7275 if ( pPCHSGeometry->cCylinders != pDisk->PCHSGeometry.cCylinders
7276 || pPCHSGeometry->cHeads != pDisk->PCHSGeometry.cHeads
7277 || pPCHSGeometry->cSectors != pDisk->PCHSGeometry.cSectors)
7278 {
7279 /* Only update geometry if it is changed. Avoids similar checks
7280 * in every backend. Most of the time the new geometry is set
7281 * to the previous values, so no need to go through the hassle
7282 * of updating an image which could be opened in read-only mode
7283 * right now. */
7284 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pBackendData,
7285 pPCHSGeometry);
7286
7287 /* Cache new geometry values in any case. */
7288 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
7289 &pDisk->PCHSGeometry);
7290 if (RT_FAILURE(rc2))
7291 {
7292 pDisk->PCHSGeometry.cCylinders = 0;
7293 pDisk->PCHSGeometry.cHeads = 0;
7294 pDisk->PCHSGeometry.cSectors = 0;
7295 }
7296 else
7297 {
7298 /* Make sure the CHS geometry is properly clipped. */
7299 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 255);
7300 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
7301 }
7302 }
7303 }
7304 else
7305 {
7306 VDGEOMETRY PCHS;
7307 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
7308 &PCHS);
7309 if ( RT_FAILURE(rc)
7310 || pPCHSGeometry->cCylinders != PCHS.cCylinders
7311 || pPCHSGeometry->cHeads != PCHS.cHeads
7312 || pPCHSGeometry->cSectors != PCHS.cSectors)
7313 {
7314 /* Only update geometry if it is changed. Avoids similar checks
7315 * in every backend. Most of the time the new geometry is set
7316 * to the previous values, so no need to go through the hassle
7317 * of updating an image which could be opened in read-only mode
7318 * right now. */
7319 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pBackendData,
7320 pPCHSGeometry);
7321 }
7322 }
7323 } while (0);
7324
7325 if (RT_UNLIKELY(fLockWrite))
7326 {
7327 rc2 = vdThreadFinishWrite(pDisk);
7328 AssertRC(rc2);
7329 }
7330
7331 LogFlowFunc(("returns %Rrc\n", rc));
7332 return rc;
7333}
7334
7335/**
7336 * Get virtual disk LCHS geometry stored in HDD container.
7337 *
7338 * @returns VBox status code.
7339 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7340 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
7341 * @param pDisk Pointer to HDD container.
7342 * @param nImage Image number, counts from 0. 0 is always base image of container.
7343 * @param pLCHSGeometry Where to store LCHS geometry. Not NULL.
7344 */
7345VBOXDDU_DECL(int) VDGetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
7346 PVDGEOMETRY pLCHSGeometry)
7347{
7348 int rc = VINF_SUCCESS;
7349 int rc2;
7350 bool fLockRead = false;
7351
7352 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p\n",
7353 pDisk, nImage, pLCHSGeometry));
7354 do
7355 {
7356 /* sanity check */
7357 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7358 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7359
7360 /* Check arguments. */
7361 AssertMsgBreakStmt(VALID_PTR(pLCHSGeometry),
7362 ("pLCHSGeometry=%#p\n", pLCHSGeometry),
7363 rc = VERR_INVALID_PARAMETER);
7364
7365 rc2 = vdThreadStartRead(pDisk);
7366 AssertRC(rc2);
7367 fLockRead = true;
7368
7369 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7370 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7371
7372 if (pImage == pDisk->pLast)
7373 {
7374 /* Use cached information if possible. */
7375 if (pDisk->LCHSGeometry.cCylinders != 0)
7376 *pLCHSGeometry = pDisk->LCHSGeometry;
7377 else
7378 rc = VERR_VD_GEOMETRY_NOT_SET;
7379 }
7380 else
7381 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
7382 pLCHSGeometry);
7383 } while (0);
7384
7385 if (RT_UNLIKELY(fLockRead))
7386 {
7387 rc2 = vdThreadFinishRead(pDisk);
7388 AssertRC(rc2);
7389 }
7390
7391 LogFlowFunc((": %Rrc (LCHS=%u/%u/%u)\n", rc,
7392 pDisk->LCHSGeometry.cCylinders, pDisk->LCHSGeometry.cHeads,
7393 pDisk->LCHSGeometry.cSectors));
7394 return rc;
7395}
7396
7397/**
7398 * Store virtual disk LCHS geometry in HDD container.
7399 *
7400 * Note that in case of unrecoverable error all images in HDD container will be closed.
7401 *
7402 * @returns VBox status code.
7403 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7404 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
7405 * @param pDisk Pointer to HDD container.
7406 * @param nImage Image number, counts from 0. 0 is always base image of container.
7407 * @param pLCHSGeometry Where to load LCHS geometry from. Not NULL.
7408 */
7409VBOXDDU_DECL(int) VDSetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
7410 PCVDGEOMETRY pLCHSGeometry)
7411{
7412 int rc = VINF_SUCCESS;
7413 int rc2;
7414 bool fLockWrite = false;
7415
7416 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
7417 pDisk, nImage, pLCHSGeometry, pLCHSGeometry->cCylinders,
7418 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
7419 do
7420 {
7421 /* sanity check */
7422 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7423 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7424
7425 /* Check arguments. */
7426 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
7427 && pLCHSGeometry->cHeads <= 255
7428 && pLCHSGeometry->cSectors <= 63,
7429 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
7430 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
7431 pLCHSGeometry->cSectors),
7432 rc = VERR_INVALID_PARAMETER);
7433
7434 rc2 = vdThreadStartWrite(pDisk);
7435 AssertRC(rc2);
7436 fLockWrite = true;
7437
7438 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7439 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7440
7441 if (pImage == pDisk->pLast)
7442 {
7443 if ( pLCHSGeometry->cCylinders != pDisk->LCHSGeometry.cCylinders
7444 || pLCHSGeometry->cHeads != pDisk->LCHSGeometry.cHeads
7445 || pLCHSGeometry->cSectors != pDisk->LCHSGeometry.cSectors)
7446 {
7447 /* Only update geometry if it is changed. Avoids similar checks
7448 * in every backend. Most of the time the new geometry is set
7449 * to the previous values, so no need to go through the hassle
7450 * of updating an image which could be opened in read-only mode
7451 * right now. */
7452 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pBackendData,
7453 pLCHSGeometry);
7454
7455 /* Cache new geometry values in any case. */
7456 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
7457 &pDisk->LCHSGeometry);
7458 if (RT_FAILURE(rc2))
7459 {
7460 pDisk->LCHSGeometry.cCylinders = 0;
7461 pDisk->LCHSGeometry.cHeads = 0;
7462 pDisk->LCHSGeometry.cSectors = 0;
7463 }
7464 else
7465 {
7466 /* Make sure the CHS geometry is properly clipped. */
7467 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
7468 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
7469 }
7470 }
7471 }
7472 else
7473 {
7474 VDGEOMETRY LCHS;
7475 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
7476 &LCHS);
7477 if ( RT_FAILURE(rc)
7478 || pLCHSGeometry->cCylinders != LCHS.cCylinders
7479 || pLCHSGeometry->cHeads != LCHS.cHeads
7480 || pLCHSGeometry->cSectors != LCHS.cSectors)
7481 {
7482 /* Only update geometry if it is changed. Avoids similar checks
7483 * in every backend. Most of the time the new geometry is set
7484 * to the previous values, so no need to go through the hassle
7485 * of updating an image which could be opened in read-only mode
7486 * right now. */
7487 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pBackendData,
7488 pLCHSGeometry);
7489 }
7490 }
7491 } while (0);
7492
7493 if (RT_UNLIKELY(fLockWrite))
7494 {
7495 rc2 = vdThreadFinishWrite(pDisk);
7496 AssertRC(rc2);
7497 }
7498
7499 LogFlowFunc(("returns %Rrc\n", rc));
7500 return rc;
7501}
7502
7503/**
7504 * Get version of image in HDD container.
7505 *
7506 * @returns VBox status code.
7507 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7508 * @param pDisk Pointer to HDD container.
7509 * @param nImage Image number, counts from 0. 0 is always base image of container.
7510 * @param puVersion Where to store the image version.
7511 */
7512VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
7513 unsigned *puVersion)
7514{
7515 int rc = VINF_SUCCESS;
7516 int rc2;
7517 bool fLockRead = false;
7518
7519 LogFlowFunc(("pDisk=%#p nImage=%u puVersion=%#p\n",
7520 pDisk, nImage, puVersion));
7521 do
7522 {
7523 /* sanity check */
7524 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7525 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7526
7527 /* Check arguments. */
7528 AssertMsgBreakStmt(VALID_PTR(puVersion),
7529 ("puVersion=%#p\n", puVersion),
7530 rc = VERR_INVALID_PARAMETER);
7531
7532 rc2 = vdThreadStartRead(pDisk);
7533 AssertRC(rc2);
7534 fLockRead = true;
7535
7536 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7537 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7538
7539 *puVersion = pImage->Backend->pfnGetVersion(pImage->pBackendData);
7540 } while (0);
7541
7542 if (RT_UNLIKELY(fLockRead))
7543 {
7544 rc2 = vdThreadFinishRead(pDisk);
7545 AssertRC(rc2);
7546 }
7547
7548 LogFlowFunc(("returns %Rrc uVersion=%#x\n", rc, *puVersion));
7549 return rc;
7550}
7551
7552/**
7553 * List the capabilities of image backend in HDD container.
7554 *
7555 * @returns VBox status code.
7556 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7557 * @param pDisk Pointer to the HDD container.
7558 * @param nImage Image number, counts from 0. 0 is always base image of container.
7559 * @param pbackendInfo Where to store the backend information.
7560 */
7561VBOXDDU_DECL(int) VDBackendInfoSingle(PVBOXHDD pDisk, unsigned nImage,
7562 PVDBACKENDINFO pBackendInfo)
7563{
7564 int rc = VINF_SUCCESS;
7565 int rc2;
7566 bool fLockRead = false;
7567
7568 LogFlowFunc(("pDisk=%#p nImage=%u pBackendInfo=%#p\n",
7569 pDisk, nImage, pBackendInfo));
7570 do
7571 {
7572 /* sanity check */
7573 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7574 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7575
7576 /* Check arguments. */
7577 AssertMsgBreakStmt(VALID_PTR(pBackendInfo),
7578 ("pBackendInfo=%#p\n", pBackendInfo),
7579 rc = VERR_INVALID_PARAMETER);
7580
7581 rc2 = vdThreadStartRead(pDisk);
7582 AssertRC(rc2);
7583 fLockRead = true;
7584
7585 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7586 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7587
7588 pBackendInfo->pszBackend = pImage->Backend->pszBackendName;
7589 pBackendInfo->uBackendCaps = pImage->Backend->uBackendCaps;
7590 pBackendInfo->paFileExtensions = pImage->Backend->paFileExtensions;
7591 pBackendInfo->paConfigInfo = pImage->Backend->paConfigInfo;
7592 } while (0);
7593
7594 if (RT_UNLIKELY(fLockRead))
7595 {
7596 rc2 = vdThreadFinishRead(pDisk);
7597 AssertRC(rc2);
7598 }
7599
7600 LogFlowFunc(("returns %Rrc\n", rc));
7601 return rc;
7602}
7603
7604/**
7605 * Get flags of image in HDD container.
7606 *
7607 * @returns VBox status code.
7608 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7609 * @param pDisk Pointer to HDD container.
7610 * @param nImage Image number, counts from 0. 0 is always base image of container.
7611 * @param puImageFlags Where to store the image flags.
7612 */
7613VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage,
7614 unsigned *puImageFlags)
7615{
7616 int rc = VINF_SUCCESS;
7617 int rc2;
7618 bool fLockRead = false;
7619
7620 LogFlowFunc(("pDisk=%#p nImage=%u puImageFlags=%#p\n",
7621 pDisk, nImage, puImageFlags));
7622 do
7623 {
7624 /* sanity check */
7625 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7626 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7627
7628 /* Check arguments. */
7629 AssertMsgBreakStmt(VALID_PTR(puImageFlags),
7630 ("puImageFlags=%#p\n", puImageFlags),
7631 rc = VERR_INVALID_PARAMETER);
7632
7633 rc2 = vdThreadStartRead(pDisk);
7634 AssertRC(rc2);
7635 fLockRead = true;
7636
7637 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7638 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7639
7640 *puImageFlags = pImage->uImageFlags;
7641 } while (0);
7642
7643 if (RT_UNLIKELY(fLockRead))
7644 {
7645 rc2 = vdThreadFinishRead(pDisk);
7646 AssertRC(rc2);
7647 }
7648
7649 LogFlowFunc(("returns %Rrc uImageFlags=%#x\n", rc, *puImageFlags));
7650 return rc;
7651}
7652
7653/**
7654 * Get open flags of image in HDD container.
7655 *
7656 * @returns VBox status code.
7657 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7658 * @param pDisk Pointer to HDD container.
7659 * @param nImage Image number, counts from 0. 0 is always base image of container.
7660 * @param puOpenFlags Where to store the image open flags.
7661 */
7662VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
7663 unsigned *puOpenFlags)
7664{
7665 int rc = VINF_SUCCESS;
7666 int rc2;
7667 bool fLockRead = false;
7668
7669 LogFlowFunc(("pDisk=%#p nImage=%u puOpenFlags=%#p\n",
7670 pDisk, nImage, puOpenFlags));
7671 do
7672 {
7673 /* sanity check */
7674 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7675 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7676
7677 /* Check arguments. */
7678 AssertMsgBreakStmt(VALID_PTR(puOpenFlags),
7679 ("puOpenFlags=%#p\n", puOpenFlags),
7680 rc = VERR_INVALID_PARAMETER);
7681
7682 rc2 = vdThreadStartRead(pDisk);
7683 AssertRC(rc2);
7684 fLockRead = true;
7685
7686 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7687 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7688
7689 *puOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
7690 } while (0);
7691
7692 if (RT_UNLIKELY(fLockRead))
7693 {
7694 rc2 = vdThreadFinishRead(pDisk);
7695 AssertRC(rc2);
7696 }
7697
7698 LogFlowFunc(("returns %Rrc uOpenFlags=%#x\n", rc, *puOpenFlags));
7699 return rc;
7700}
7701
7702/**
7703 * Set open flags of image in HDD container.
7704 * This operation may cause file locking changes and/or files being reopened.
7705 * Note that in case of unrecoverable error all images in HDD container will be closed.
7706 *
7707 * @returns VBox status code.
7708 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7709 * @param pDisk Pointer to HDD container.
7710 * @param nImage Image number, counts from 0. 0 is always base image of container.
7711 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
7712 */
7713VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
7714 unsigned uOpenFlags)
7715{
7716 int rc;
7717 int rc2;
7718 bool fLockWrite = false;
7719
7720 LogFlowFunc(("pDisk=%#p uOpenFlags=%#u\n", pDisk, uOpenFlags));
7721 do
7722 {
7723 /* sanity check */
7724 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7725 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7726
7727 /* Check arguments. */
7728 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
7729 ("uOpenFlags=%#x\n", uOpenFlags),
7730 rc = VERR_INVALID_PARAMETER);
7731
7732 rc2 = vdThreadStartWrite(pDisk);
7733 AssertRC(rc2);
7734 fLockWrite = true;
7735
7736 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7737 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7738
7739 rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData,
7740 uOpenFlags);
7741 } while (0);
7742
7743 if (RT_UNLIKELY(fLockWrite))
7744 {
7745 rc2 = vdThreadFinishWrite(pDisk);
7746 AssertRC(rc2);
7747 }
7748
7749 LogFlowFunc(("returns %Rrc\n", rc));
7750 return rc;
7751}
7752
7753/**
7754 * Get base filename of image in HDD container. Some image formats use
7755 * other filenames as well, so don't use this for anything but informational
7756 * purposes.
7757 *
7758 * @returns VBox status code.
7759 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7760 * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
7761 * @param pDisk Pointer to HDD container.
7762 * @param nImage Image number, counts from 0. 0 is always base image of container.
7763 * @param pszFilename Where to store the image file name.
7764 * @param cbFilename Size of buffer pszFilename points to.
7765 */
7766VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
7767 char *pszFilename, unsigned cbFilename)
7768{
7769 int rc;
7770 int rc2;
7771 bool fLockRead = false;
7772
7773 LogFlowFunc(("pDisk=%#p nImage=%u pszFilename=%#p cbFilename=%u\n",
7774 pDisk, nImage, pszFilename, cbFilename));
7775 do
7776 {
7777 /* sanity check */
7778 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7779 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7780
7781 /* Check arguments. */
7782 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
7783 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
7784 rc = VERR_INVALID_PARAMETER);
7785 AssertMsgBreakStmt(cbFilename,
7786 ("cbFilename=%u\n", cbFilename),
7787 rc = VERR_INVALID_PARAMETER);
7788
7789 rc2 = vdThreadStartRead(pDisk);
7790 AssertRC(rc2);
7791 fLockRead = true;
7792
7793 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7794 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7795
7796 size_t cb = strlen(pImage->pszFilename);
7797 if (cb <= cbFilename)
7798 {
7799 strcpy(pszFilename, pImage->pszFilename);
7800 rc = VINF_SUCCESS;
7801 }
7802 else
7803 {
7804 strncpy(pszFilename, pImage->pszFilename, cbFilename - 1);
7805 pszFilename[cbFilename - 1] = '\0';
7806 rc = VERR_BUFFER_OVERFLOW;
7807 }
7808 } while (0);
7809
7810 if (RT_UNLIKELY(fLockRead))
7811 {
7812 rc2 = vdThreadFinishRead(pDisk);
7813 AssertRC(rc2);
7814 }
7815
7816 LogFlowFunc(("returns %Rrc, pszFilename=\"%s\"\n", rc, pszFilename));
7817 return rc;
7818}
7819
7820/**
7821 * Get the comment line of image in HDD container.
7822 *
7823 * @returns VBox status code.
7824 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7825 * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
7826 * @param pDisk Pointer to HDD container.
7827 * @param nImage Image number, counts from 0. 0 is always base image of container.
7828 * @param pszComment Where to store the comment string of image. NULL is ok.
7829 * @param cbComment The size of pszComment buffer. 0 is ok.
7830 */
7831VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
7832 char *pszComment, unsigned cbComment)
7833{
7834 int rc;
7835 int rc2;
7836 bool fLockRead = false;
7837
7838 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p cbComment=%u\n",
7839 pDisk, nImage, pszComment, cbComment));
7840 do
7841 {
7842 /* sanity check */
7843 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7844 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7845
7846 /* Check arguments. */
7847 AssertMsgBreakStmt(VALID_PTR(pszComment),
7848 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
7849 rc = VERR_INVALID_PARAMETER);
7850 AssertMsgBreakStmt(cbComment,
7851 ("cbComment=%u\n", cbComment),
7852 rc = VERR_INVALID_PARAMETER);
7853
7854 rc2 = vdThreadStartRead(pDisk);
7855 AssertRC(rc2);
7856 fLockRead = true;
7857
7858 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7859 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7860
7861 rc = pImage->Backend->pfnGetComment(pImage->pBackendData, pszComment,
7862 cbComment);
7863 } while (0);
7864
7865 if (RT_UNLIKELY(fLockRead))
7866 {
7867 rc2 = vdThreadFinishRead(pDisk);
7868 AssertRC(rc2);
7869 }
7870
7871 LogFlowFunc(("returns %Rrc, pszComment=\"%s\"\n", rc, pszComment));
7872 return rc;
7873}
7874
7875/**
7876 * Changes the comment line of image in HDD container.
7877 *
7878 * @returns VBox status code.
7879 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7880 * @param pDisk Pointer to HDD container.
7881 * @param nImage Image number, counts from 0. 0 is always base image of container.
7882 * @param pszComment New comment string (UTF-8). NULL is allowed to reset the comment.
7883 */
7884VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
7885 const char *pszComment)
7886{
7887 int rc;
7888 int rc2;
7889 bool fLockWrite = false;
7890
7891 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p \"%s\"\n",
7892 pDisk, nImage, pszComment, pszComment));
7893 do
7894 {
7895 /* sanity check */
7896 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7897 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7898
7899 /* Check arguments. */
7900 AssertMsgBreakStmt(VALID_PTR(pszComment) || pszComment == NULL,
7901 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
7902 rc = VERR_INVALID_PARAMETER);
7903
7904 rc2 = vdThreadStartWrite(pDisk);
7905 AssertRC(rc2);
7906 fLockWrite = true;
7907
7908 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7909 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7910
7911 rc = pImage->Backend->pfnSetComment(pImage->pBackendData, pszComment);
7912 } while (0);
7913
7914 if (RT_UNLIKELY(fLockWrite))
7915 {
7916 rc2 = vdThreadFinishWrite(pDisk);
7917 AssertRC(rc2);
7918 }
7919
7920 LogFlowFunc(("returns %Rrc\n", rc));
7921 return rc;
7922}
7923
7924
7925/**
7926 * Get UUID of image in HDD container.
7927 *
7928 * @returns VBox status code.
7929 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7930 * @param pDisk Pointer to HDD container.
7931 * @param nImage Image number, counts from 0. 0 is always base image of container.
7932 * @param pUuid Where to store the image creation UUID.
7933 */
7934VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
7935{
7936 int rc;
7937 int rc2;
7938 bool fLockRead = false;
7939
7940 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
7941 do
7942 {
7943 /* sanity check */
7944 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7945 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7946
7947 /* Check arguments. */
7948 AssertMsgBreakStmt(VALID_PTR(pUuid),
7949 ("pUuid=%#p\n", pUuid),
7950 rc = VERR_INVALID_PARAMETER);
7951
7952 rc2 = vdThreadStartRead(pDisk);
7953 AssertRC(rc2);
7954 fLockRead = true;
7955
7956 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7957 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7958
7959 rc = pImage->Backend->pfnGetUuid(pImage->pBackendData, pUuid);
7960 } while (0);
7961
7962 if (RT_UNLIKELY(fLockRead))
7963 {
7964 rc2 = vdThreadFinishRead(pDisk);
7965 AssertRC(rc2);
7966 }
7967
7968 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
7969 return rc;
7970}
7971
7972/**
7973 * Set the image's UUID. Should not be used by normal applications.
7974 *
7975 * @returns VBox status code.
7976 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7977 * @param pDisk Pointer to HDD container.
7978 * @param nImage Image number, counts from 0. 0 is always base image of container.
7979 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
7980 */
7981VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
7982{
7983 int rc;
7984 int rc2;
7985 bool fLockWrite = false;
7986
7987 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
7988 pDisk, nImage, pUuid, pUuid));
7989 do
7990 {
7991 /* sanity check */
7992 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7993 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7994
7995 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
7996 ("pUuid=%#p\n", pUuid),
7997 rc = VERR_INVALID_PARAMETER);
7998
7999 rc2 = vdThreadStartWrite(pDisk);
8000 AssertRC(rc2);
8001 fLockWrite = true;
8002
8003 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8004 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
8005
8006 RTUUID Uuid;
8007 if (!pUuid)
8008 {
8009 RTUuidCreate(&Uuid);
8010 pUuid = &Uuid;
8011 }
8012 rc = pImage->Backend->pfnSetUuid(pImage->pBackendData, pUuid);
8013 } while (0);
8014
8015 if (RT_UNLIKELY(fLockWrite))
8016 {
8017 rc2 = vdThreadFinishWrite(pDisk);
8018 AssertRC(rc2);
8019 }
8020
8021 LogFlowFunc(("returns %Rrc\n", rc));
8022 return rc;
8023}
8024
8025/**
8026 * Get last modification UUID of image in HDD container.
8027 *
8028 * @returns VBox status code.
8029 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
8030 * @param pDisk Pointer to HDD container.
8031 * @param nImage Image number, counts from 0. 0 is always base image of container.
8032 * @param pUuid Where to store the image modification UUID.
8033 */
8034VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
8035{
8036 int rc = VINF_SUCCESS;
8037 int rc2;
8038 bool fLockRead = false;
8039
8040 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
8041 do
8042 {
8043 /* sanity check */
8044 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8045 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8046
8047 /* Check arguments. */
8048 AssertMsgBreakStmt(VALID_PTR(pUuid),
8049 ("pUuid=%#p\n", pUuid),
8050 rc = VERR_INVALID_PARAMETER);
8051
8052 rc2 = vdThreadStartRead(pDisk);
8053 AssertRC(rc2);
8054 fLockRead = true;
8055
8056 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8057 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
8058
8059 rc = pImage->Backend->pfnGetModificationUuid(pImage->pBackendData,
8060 pUuid);
8061 } while (0);
8062
8063 if (RT_UNLIKELY(fLockRead))
8064 {
8065 rc2 = vdThreadFinishRead(pDisk);
8066 AssertRC(rc2);
8067 }
8068
8069 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
8070 return rc;
8071}
8072
8073/**
8074 * Set the image's last modification UUID. Should not be used by normal applications.
8075 *
8076 * @returns VBox status code.
8077 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
8078 * @param pDisk Pointer to HDD container.
8079 * @param nImage Image number, counts from 0. 0 is always base image of container.
8080 * @param pUuid New modification UUID of the image. If NULL, a new UUID is created.
8081 */
8082VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
8083{
8084 int rc;
8085 int rc2;
8086 bool fLockWrite = false;
8087
8088 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
8089 pDisk, nImage, pUuid, pUuid));
8090 do
8091 {
8092 /* sanity check */
8093 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8094 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8095
8096 /* Check arguments. */
8097 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
8098 ("pUuid=%#p\n", pUuid),
8099 rc = VERR_INVALID_PARAMETER);
8100
8101 rc2 = vdThreadStartWrite(pDisk);
8102 AssertRC(rc2);
8103 fLockWrite = true;
8104
8105 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8106 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
8107
8108 RTUUID Uuid;
8109 if (!pUuid)
8110 {
8111 RTUuidCreate(&Uuid);
8112 pUuid = &Uuid;
8113 }
8114 rc = pImage->Backend->pfnSetModificationUuid(pImage->pBackendData,
8115 pUuid);
8116 } while (0);
8117
8118 if (RT_UNLIKELY(fLockWrite))
8119 {
8120 rc2 = vdThreadFinishWrite(pDisk);
8121 AssertRC(rc2);
8122 }
8123
8124 LogFlowFunc(("returns %Rrc\n", rc));
8125 return rc;
8126}
8127
8128/**
8129 * Get parent UUID of image in HDD container.
8130 *
8131 * @returns VBox status code.
8132 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
8133 * @param pDisk Pointer to HDD container.
8134 * @param nImage Image number, counts from 0. 0 is always base image of container.
8135 * @param pUuid Where to store the parent image UUID.
8136 */
8137VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
8138 PRTUUID pUuid)
8139{
8140 int rc = VINF_SUCCESS;
8141 int rc2;
8142 bool fLockRead = false;
8143
8144 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
8145 do
8146 {
8147 /* sanity check */
8148 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8149 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8150
8151 /* Check arguments. */
8152 AssertMsgBreakStmt(VALID_PTR(pUuid),
8153 ("pUuid=%#p\n", pUuid),
8154 rc = VERR_INVALID_PARAMETER);
8155
8156 rc2 = vdThreadStartRead(pDisk);
8157 AssertRC(rc2);
8158 fLockRead = true;
8159
8160 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8161 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
8162
8163 rc = pImage->Backend->pfnGetParentUuid(pImage->pBackendData, pUuid);
8164 } while (0);
8165
8166 if (RT_UNLIKELY(fLockRead))
8167 {
8168 rc2 = vdThreadFinishRead(pDisk);
8169 AssertRC(rc2);
8170 }
8171
8172 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
8173 return rc;
8174}
8175
8176/**
8177 * Set the image's parent UUID. Should not be used by normal applications.
8178 *
8179 * @returns VBox status code.
8180 * @param pDisk Pointer to HDD container.
8181 * @param nImage Image number, counts from 0. 0 is always base image of container.
8182 * @param pUuid New parent UUID of the image. If NULL, a new UUID is created.
8183 */
8184VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
8185 PCRTUUID pUuid)
8186{
8187 int rc;
8188 int rc2;
8189 bool fLockWrite = false;
8190
8191 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
8192 pDisk, nImage, pUuid, pUuid));
8193 do
8194 {
8195 /* sanity check */
8196 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8197 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8198
8199 /* Check arguments. */
8200 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
8201 ("pUuid=%#p\n", pUuid),
8202 rc = VERR_INVALID_PARAMETER);
8203
8204 rc2 = vdThreadStartWrite(pDisk);
8205 AssertRC(rc2);
8206 fLockWrite = true;
8207
8208 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8209 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
8210
8211 RTUUID Uuid;
8212 if (!pUuid)
8213 {
8214 RTUuidCreate(&Uuid);
8215 pUuid = &Uuid;
8216 }
8217 rc = pImage->Backend->pfnSetParentUuid(pImage->pBackendData, pUuid);
8218 } while (0);
8219
8220 if (RT_UNLIKELY(fLockWrite))
8221 {
8222 rc2 = vdThreadFinishWrite(pDisk);
8223 AssertRC(rc2);
8224 }
8225
8226 LogFlowFunc(("returns %Rrc\n", rc));
8227 return rc;
8228}
8229
8230
8231/**
8232 * Debug helper - dumps all opened images in HDD container into the log file.
8233 *
8234 * @param pDisk Pointer to HDD container.
8235 */
8236VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk)
8237{
8238 int rc2;
8239 bool fLockRead = false;
8240
8241 do
8242 {
8243 /* sanity check */
8244 AssertPtrBreak(pDisk);
8245 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8246
8247 if (!pDisk->pInterfaceErrorCallbacks || !VALID_PTR(pDisk->pInterfaceErrorCallbacks->pfnMessage))
8248 pDisk->pInterfaceErrorCallbacks->pfnMessage = vdLogMessage;
8249
8250 rc2 = vdThreadStartRead(pDisk);
8251 AssertRC(rc2);
8252 fLockRead = true;
8253
8254 vdMessageWrapper(pDisk, "--- Dumping VD Disk, Images=%u\n", pDisk->cImages);
8255 for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
8256 {
8257 vdMessageWrapper(pDisk, "Dumping VD image \"%s\" (Backend=%s)\n",
8258 pImage->pszFilename, pImage->Backend->pszBackendName);
8259 pImage->Backend->pfnDump(pImage->pBackendData);
8260 }
8261 } while (0);
8262
8263 if (RT_UNLIKELY(fLockRead))
8264 {
8265 rc2 = vdThreadFinishRead(pDisk);
8266 AssertRC(rc2);
8267 }
8268}
8269
8270
8271VBOXDDU_DECL(int) VDAsyncRead(PVBOXHDD pDisk, uint64_t uOffset, size_t cbRead,
8272 PCRTSGBUF pcSgBuf,
8273 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
8274 void *pvUser1, void *pvUser2)
8275{
8276 int rc = VERR_VD_BLOCK_FREE;
8277 int rc2;
8278 bool fLockRead = false;
8279 PVDIOCTX pIoCtx = NULL;
8280
8281 LogFlowFunc(("pDisk=%#p uOffset=%llu pcSgBuf=%#p cbRead=%zu pvUser1=%#p pvUser2=%#p\n",
8282 pDisk, uOffset, pcSgBuf, cbRead, pvUser1, pvUser2));
8283
8284 do
8285 {
8286 /* sanity check */
8287 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8288 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8289
8290 /* Check arguments. */
8291 AssertMsgBreakStmt(cbRead,
8292 ("cbRead=%zu\n", cbRead),
8293 rc = VERR_INVALID_PARAMETER);
8294 AssertMsgBreakStmt(VALID_PTR(pcSgBuf),
8295 ("pcSgBuf=%#p\n", pcSgBuf),
8296 rc = VERR_INVALID_PARAMETER);
8297
8298 rc2 = vdThreadStartRead(pDisk);
8299 AssertRC(rc2);
8300 fLockRead = true;
8301
8302 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
8303 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
8304 uOffset, cbRead, pDisk->cbSize),
8305 rc = VERR_INVALID_PARAMETER);
8306 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
8307
8308 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_READ, uOffset,
8309 cbRead, pDisk->pLast, pcSgBuf,
8310 pfnComplete, pvUser1, pvUser2,
8311 NULL, vdReadHelperAsync);
8312 if (!pIoCtx)
8313 {
8314 rc = VERR_NO_MEMORY;
8315 break;
8316 }
8317
8318 rc = vdIoCtxProcess(pIoCtx);
8319 if (rc == VINF_VD_ASYNC_IO_FINISHED)
8320 {
8321 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
8322 vdIoCtxFree(pDisk, pIoCtx);
8323 else
8324 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
8325 }
8326 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
8327 vdIoCtxFree(pDisk, pIoCtx);
8328
8329 } while (0);
8330
8331 if (RT_UNLIKELY(fLockRead) && ( rc == VINF_VD_ASYNC_IO_FINISHED
8332 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
8333 {
8334 rc2 = vdThreadFinishRead(pDisk);
8335 AssertRC(rc2);
8336 }
8337
8338 LogFlowFunc(("returns %Rrc\n", rc));
8339 return rc;
8340}
8341
8342
8343VBOXDDU_DECL(int) VDAsyncWrite(PVBOXHDD pDisk, uint64_t uOffset, size_t cbWrite,
8344 PCRTSGBUF pcSgBuf,
8345 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
8346 void *pvUser1, void *pvUser2)
8347{
8348 int rc;
8349 int rc2;
8350 bool fLockWrite = false;
8351 PVDIOCTX pIoCtx = NULL;
8352
8353 LogFlowFunc(("pDisk=%#p uOffset=%llu cSgBuf=%#p cbWrite=%zu pvUser1=%#p pvUser2=%#p\n",
8354 pDisk, uOffset, pcSgBuf, cbWrite, pvUser1, pvUser2));
8355 do
8356 {
8357 /* sanity check */
8358 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8359 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8360
8361 /* Check arguments. */
8362 AssertMsgBreakStmt(cbWrite,
8363 ("cbWrite=%zu\n", cbWrite),
8364 rc = VERR_INVALID_PARAMETER);
8365 AssertMsgBreakStmt(VALID_PTR(pcSgBuf),
8366 ("pcSgBuf=%#p\n", pcSgBuf),
8367 rc = VERR_INVALID_PARAMETER);
8368
8369 rc2 = vdThreadStartWrite(pDisk);
8370 AssertRC(rc2);
8371 fLockWrite = true;
8372
8373 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
8374 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
8375 uOffset, cbWrite, pDisk->cbSize),
8376 rc = VERR_INVALID_PARAMETER);
8377 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
8378
8379 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_WRITE, uOffset,
8380 cbWrite, pDisk->pLast, pcSgBuf,
8381 pfnComplete, pvUser1, pvUser2,
8382 NULL, vdWriteHelperAsync);
8383 if (!pIoCtx)
8384 {
8385 rc = VERR_NO_MEMORY;
8386 break;
8387 }
8388
8389 rc = vdIoCtxProcess(pIoCtx);
8390 if (rc == VINF_VD_ASYNC_IO_FINISHED)
8391 {
8392 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
8393 vdIoCtxFree(pDisk, pIoCtx);
8394 else
8395 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
8396 }
8397 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
8398 vdIoCtxFree(pDisk, pIoCtx);
8399 } while (0);
8400
8401 if (RT_UNLIKELY(fLockWrite) && ( rc == VINF_VD_ASYNC_IO_FINISHED
8402 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
8403 {
8404 rc2 = vdThreadFinishWrite(pDisk);
8405 AssertRC(rc2);
8406 }
8407
8408 LogFlowFunc(("returns %Rrc\n", rc));
8409 return rc;
8410}
8411
8412
8413VBOXDDU_DECL(int) VDAsyncFlush(PVBOXHDD pDisk, PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
8414 void *pvUser1, void *pvUser2)
8415{
8416 int rc;
8417 int rc2;
8418 bool fLockWrite = false;
8419 PVDIOCTX pIoCtx = NULL;
8420
8421 LogFlowFunc(("pDisk=%#p\n", pDisk));
8422
8423 do
8424 {
8425 /* sanity check */
8426 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8427 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8428
8429 rc2 = vdThreadStartWrite(pDisk);
8430 AssertRC(rc2);
8431 fLockWrite = true;
8432
8433 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
8434
8435 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_FLUSH, 0,
8436 0, pDisk->pLast, NULL,
8437 pfnComplete, pvUser1, pvUser2,
8438 NULL, vdFlushHelperAsync);
8439 if (!pIoCtx)
8440 {
8441 rc = VERR_NO_MEMORY;
8442 break;
8443 }
8444
8445 rc = vdIoCtxProcess(pIoCtx);
8446 if (rc == VINF_VD_ASYNC_IO_FINISHED)
8447 {
8448 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
8449 vdIoCtxFree(pDisk, pIoCtx);
8450 else
8451 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
8452 }
8453 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
8454 vdIoCtxFree(pDisk, pIoCtx);
8455 } while (0);
8456
8457 if (RT_UNLIKELY(fLockWrite) && ( rc == VINF_VD_ASYNC_IO_FINISHED
8458 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
8459 {
8460 rc2 = vdThreadFinishWrite(pDisk);
8461 AssertRC(rc2);
8462 }
8463
8464 LogFlowFunc(("returns %Rrc\n", rc));
8465 return rc;
8466}
8467
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