VirtualBox

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

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

Storage: Fix read from wrong snapshots. Happens only with formats which need to read metadata from the disk and for disks with more than 1 snapshot

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

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