VirtualBox

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

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

VD: Interface cleanup. Merge the two involved structures (generic interface descriptor and callback table) into one, remove the duplicated interface wrappers in the backends and move the interface definitions into separate headers separating public and private interfaces.

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