VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/VBoxHDD.cpp@ 32641

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

Storage/VBoxHDD: small optimization in the error path, no need to close images which were never opened.

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