VirtualBox

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

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

VD: Fix possible race condition which can lead to hanging I/O requests

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