VirtualBox

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

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

VD: Update the modification UUID during async I/O

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

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