VirtualBox

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

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

VD: Finish async I/O support for VMDK. Still disabled by default until images are tested which were not created by VirtualBox

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