VirtualBox

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

Last change on this file since 31495 was 31380, checked in by vboxsync, 15 years ago

VmdkHDDCore, VBoxHDD: warnings

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 235.6 KB
Line 
1/* $Id: VBoxHDD.cpp 31380 2010-08-05 07:33:32Z vboxsync $ */
2/** @file
3 * VBoxHDD - VBox HDD Container implementation.
4 */
5
6/*
7 * Copyright (C) 2006-2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_VD
22#include <VBox/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
1493 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
1494
1495 AssertPtr(pIoCtx->pIoCtxParent);
1496 Assert(!pIoCtx->pIoCtxParent->pIoCtxParent);
1497
1498 if (cbPostRead)
1499 {
1500 /* Figure out how much we cannnot read from the image, because
1501 * the last block to write might exceed the nominal size of the
1502 * image for technical reasons. */
1503 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
1504 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
1505
1506 /* If we have data to be written, use that instead of reading
1507 * data from the image. */
1508 if (cbWrite > cbThisWrite)
1509 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
1510
1511 /* The rest must be read from the image. */
1512 cbReadImage = cbPostRead - cbWriteCopy - cbFill;
1513 }
1514
1515 pIoCtx->Type.Child.Write.Optimized.cbFill = cbFill;
1516 pIoCtx->Type.Child.Write.Optimized.cbWriteCopy = cbWriteCopy;
1517 pIoCtx->Type.Child.Write.Optimized.cbReadImage = cbReadImage;
1518
1519 /* Read the entire data of the block so that we can compare whether it will
1520 * be modified by the write or not. */
1521 pIoCtx->cbTransferLeft = cbPreRead + cbThisWrite + cbPostRead - cbFill;
1522 pIoCtx->cbTransfer = pIoCtx->cbTransferLeft;
1523 pIoCtx->uOffset -= cbPreRead;
1524
1525 /* Next step */
1526 pIoCtx->pfnIoCtxTransferNext = vdWriteHelperOptimizedPreReadAsync;
1527 return VINF_SUCCESS;
1528}
1529
1530/**
1531 * internal: write buffer to the image, taking care of block boundaries and
1532 * write optimizations - async version.
1533 */
1534static int vdWriteHelperAsync(PVDIOCTX pIoCtx)
1535{
1536 int rc;
1537 size_t cbWrite = pIoCtx->cbTransfer;
1538 uint64_t uOffset = pIoCtx->uOffset;
1539 PVDIMAGE pImage = pIoCtx->pImage;
1540 PVBOXHDD pDisk = pIoCtx->pDisk;
1541 unsigned fWrite;
1542 size_t cbThisWrite;
1543 size_t cbPreRead, cbPostRead;
1544
1545 /* Loop until all written. */
1546 do
1547 {
1548 /* Try to write the possibly partial block to the last opened image.
1549 * This works when the block is already allocated in this image or
1550 * if it is a full-block write (and allocation isn't suppressed below).
1551 * For image formats which don't support zero blocks, it's beneficial
1552 * to avoid unnecessarily allocating unchanged blocks. This prevents
1553 * unwanted expanding of images. VMDK is an example. */
1554 cbThisWrite = cbWrite;
1555 fWrite = (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
1556 ? 0 : VD_WRITE_NO_ALLOC;
1557 rc = pImage->Backend->pfnAsyncWrite(pImage->pvBackendData, uOffset,
1558 cbThisWrite, pIoCtx,
1559 &cbThisWrite, &cbPreRead,
1560 &cbPostRead, fWrite);
1561 if (rc == VERR_VD_BLOCK_FREE)
1562 {
1563 /*
1564 * If there is a growing request already put this one onto the waiting list.
1565 * It will be restarted if the current request completes.
1566 */
1567 if (ASMAtomicReadBool(&pDisk->fGrowing))
1568 {
1569 PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
1570 AssertPtr(pDeferred);
1571
1572 LogFlowFunc(("Deferring write pIoCtx=%#p\n", pIoCtx));
1573
1574 Assert(!pIoCtx->pIoCtxParent && !pIoCtx->fBlocked);
1575
1576 RTListInit(&pDeferred->NodeDeferred);
1577 pDeferred->pIoCtx = pIoCtx;
1578 RTListAppend(&pDisk->ListWriteGrowing, &pDeferred->NodeDeferred);
1579 pIoCtx->fBlocked = true;
1580 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1581 break;
1582 }
1583 else
1584 {
1585 /*
1586 * Allocate segment and buffer in one go.
1587 * A bit hackish but avoids the need to allocate memory twice.
1588 */
1589 PRTSGSEG pTmp = (PRTSGSEG)RTMemAlloc(cbPreRead + cbThisWrite + cbPostRead + sizeof(RTSGSEG));
1590 AssertBreakStmt(VALID_PTR(pTmp), rc = VERR_NO_MEMORY);
1591
1592 pTmp->pvSeg = pTmp + 1;
1593 pTmp->cbSeg = cbPreRead + cbThisWrite + cbPostRead;
1594
1595 PVDIOCTX pIoCtxWrite = vdIoCtxChildAlloc(pDisk, VDIOCTXTXDIR_WRITE,
1596 uOffset, pTmp->cbSeg,
1597 pTmp, 1,
1598 pIoCtx, cbThisWrite,
1599 cbWrite,
1600 pTmp,
1601 (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
1602 ? vdWriteHelperStandardAsync
1603 : vdWriteHelperOptimizedAsync);
1604 if (!VALID_PTR(pIoCtxWrite))
1605 {
1606 RTMemTmpFree(pTmp);
1607 rc = VERR_NO_MEMORY;
1608 break;
1609 }
1610
1611 /* Set the state to growing. */
1612 LogFlowFunc(("Disk is growing because of pIoCtx=%#p pIoCtxWrite=%#p\n",
1613
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 {
1829 fOpen |= RTFILE_O_READWRITE;
1830
1831 if (uOpenFlags & VD_INTERFACEASYNCIO_OPEN_FLAGS_DONT_LOCK)
1832 fOpen |= RTFILE_O_DENY_NONE;
1833 else
1834 fOpen |= RTFILE_O_DENY_WRITE;
1835 }
1836
1837 if (uOpenFlags & VD_INTERFACEASYNCIO_OPEN_FLAGS_CREATE)
1838 fOpen |= RTFILE_O_CREATE;
1839 else
1840 fOpen |= RTFILE_O_OPEN;
1841
1842 /* Open the file. */
1843 int rc = RTFileOpen(&pStorage->File, pszLocation, fOpen);
1844 if (RT_SUCCESS(rc))
1845 {
1846 *ppStorage = pStorage;
1847 return VINF_SUCCESS;
1848 }
1849
1850 RTMemFree(pStorage);
1851 return rc;
1852}
1853
1854/**
1855 * VD async I/O interface close callback.
1856 */
1857static int vdAsyncIOClose(void *pvUser, void *pvStorage)
1858{
1859 PVDIASYNCIOSTORAGE pStorage = (PVDIASYNCIOSTORAGE)pvStorage;
1860
1861 RTFileClose(pStorage->File);
1862 RTMemFree(pStorage);
1863 return VINF_SUCCESS;
1864}
1865
1866/**
1867 * VD async I/O interface callback for retrieving the file size.
1868 */
1869static int vdAsyncIOGetSize(void *pvUser, void *pvStorage, uint64_t *pcbSize)
1870{
1871 PVDIASYNCIOSTORAGE pStorage = (PVDIASYNCIOSTORAGE)pvStorage;
1872
1873 return RTFileGetSize(pStorage->File, pcbSize);
1874}
1875
1876/**
1877 * VD async I/O interface callback for setting the file size.
1878 */
1879static int vdAsyncIOSetSize(void *pvUser, void *pvStorage, uint64_t cbSize)
1880{
1881 PVDIASYNCIOSTORAGE pStorage = (PVDIASYNCIOSTORAGE)pvStorage;
1882
1883 return RTFileSetSize(pStorage->File, cbSize);
1884}
1885
1886/**
1887 * VD async I/O interface callback for a synchronous write to the file.
1888 */
1889static int vdAsyncIOWriteSync(void *pvUser, void *pvStorage, uint64_t uOffset,
1890 size_t cbWrite, const void *pvBuf, size_t *pcbWritten)
1891{
1892 PVDIASYNCIOSTORAGE pStorage = (PVDIASYNCIOSTORAGE)pvStorage;
1893
1894 return RTFileWriteAt(pStorage->File, uOffset, pvBuf, cbWrite, pcbWritten);
1895}
1896
1897/**
1898 * VD async I/O interface callback for a synchronous read from the file.
1899 */
1900static int vdAsyncIOReadSync(void *pvUser, void *pvStorage, uint64_t uOffset,
1901 size_t cbRead, void *pvBuf, size_t *pcbRead)
1902{
1903 PVDIASYNCIOSTORAGE pStorage = (PVDIASYNCIOSTORAGE)pvStorage;
1904
1905 return RTFileReadAt(pStorage->File, uOffset, pvBuf, cbRead, pcbRead);
1906}
1907
1908/**
1909 * VD async I/O interface callback for a synchronous flush of the file data.
1910 */
1911static int vdAsyncIOFlushSync(void *pvUser, void *pvStorage)
1912{
1913 PVDIASYNCIOSTORAGE pStorage = (PVDIASYNCIOSTORAGE)pvStorage;
1914
1915 return RTFileFlush(pStorage->File);
1916}
1917
1918/**
1919 * VD async I/O interface callback for a asynchronous read from the file.
1920 */
1921static int vdAsyncIOReadAsync(void *pvUser, void *pStorage, uint64_t uOffset,
1922 PCRTSGSEG paSegments, size_t cSegments,
1923 size_t cbRead, void *pvCompletion,
1924 void **ppTask)
1925{
1926 return VERR_NOT_IMPLEMENTED;
1927}
1928
1929/**
1930 * VD async I/O interface callback for a asynchronous write to the file.
1931 */
1932static int vdAsyncIOWriteAsync(void *pvUser, void *pStorage, uint64_t uOffset,
1933 PCRTSGSEG paSegments, size_t cSegments,
1934 size_t cbWrite, void *pvCompletion,
1935 void **ppTask)
1936{
1937 return VERR_NOT_IMPLEMENTED;
1938}
1939
1940/**
1941 * VD async I/O interface callback for a asynchronous flush of the file data.
1942 */
1943static int vdAsyncIOFlushAsync(void *pvUser, void *pStorage,
1944 void *pvCompletion, void **ppTask)
1945{
1946 return VERR_NOT_IMPLEMENTED;
1947}
1948
1949/**
1950 * Internal - Continues an I/O context after
1951 * it was halted because of an active transfer.
1952 */
1953static int vdIoCtxContinue(PVDIOCTX pIoCtx, int rcReq)
1954{
1955 PVBOXHDD pDisk = pIoCtx->pDisk;
1956 int rc = VINF_SUCCESS;
1957
1958 if (RT_FAILURE(rcReq))
1959 ASMAtomicCmpXchgS32(&pIoCtx->rcReq, rcReq, VINF_SUCCESS);
1960
1961 if (!pIoCtx->fBlocked)
1962 {
1963 /* Continue the transfer */
1964 rc = vdIoCtxProcess(pIoCtx);
1965
1966 if ( rc == VINF_VD_ASYNC_IO_FINISHED
1967 && ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
1968 {
1969 LogFlowFunc(("I/O context completed pIoCtx=%#p\n", pIoCtx));
1970 if (pIoCtx->pIoCtxParent)
1971 {
1972 PVDIOCTX pIoCtxParent = pIoCtx->pIoCtxParent;
1973
1974 LogFlowFunc(("I/O context transfered %u bytes for the parent pIoCtxParent=%p\n",
1975 pIoCtx->Type.Child.cbTransferParent, pIoCtxParent));
1976
1977 /* Update the parent state. */
1978 Assert(!pIoCtxParent->pIoCtxParent);
1979 Assert(pIoCtx->enmTxDir == VDIOCTXTXDIR_WRITE);
1980 Assert(pIoCtxParent->cbTransferLeft >= pIoCtx->Type.Child.cbTransferParent);
1981 ASMAtomicSubU32(&pIoCtxParent->cbTransferLeft, pIoCtx->Type.Child.cbTransferParent);
1982
1983 if (RT_FAILURE(pIoCtx->rcReq))
1984 ASMAtomicCmpXchgS32(&pIoCtxParent->rcReq, pIoCtx->rcReq, VINF_SUCCESS);
1985
1986 /*
1987 * A completed child write means that we finished growing the image.
1988 * We have to process any pending writes now.
1989 */
1990 Assert(pDisk->fGrowing);
1991 ASMAtomicWriteBool(&pDisk->fGrowing, false);
1992
1993 /* Unblock the parent */
1994 pIoCtxParent->fBlocked = false;
1995
1996 rc = vdIoCtxProcess(pIoCtxParent);
1997
1998 if ( rc == VINF_VD_ASYNC_IO_FINISHED
1999 && ASMAtomicCmpXchgBool(&pIoCtxParent->fComplete, true, false))
2000 {
2001 LogFlowFunc(("Parent I/O context completed pIoCtxParent=%#p rcReq=%Rrc\n", pIoCtxParent, pIoCtxParent->rcReq));
2002 pIoCtxParent->Type.Root.pfnComplete(pIoCtxParent->Type.Root.pvUser1,
2003 pIoCtxParent->Type.Root.pvUser2,
2004 pIoCtxParent->rcReq);
2005 vdThreadFinishWrite(pDisk);
2006 vdIoCtxFree(pDisk, pIoCtxParent);
2007 }
2008
2009 /* Process any pending writes if the current request didn't caused another growing. */
2010 RTCritSectEnter(&pDisk->CritSect);
2011
2012 if (!RTListIsEmpty(&pDisk->ListWriteGrowing) && !pDisk->fGrowing)
2013 {
2014 RTLISTNODE ListTmp;
2015
2016 LogFlowFunc(("Before: pNext=%#p pPrev=%#p\n", pDisk->ListWriteGrowing.pNext,
2017 pDisk->ListWriteGrowing.pPrev));
2018
2019 RTListMove(&ListTmp, &pDisk->ListWriteGrowing);
2020
2021 LogFlowFunc(("After: pNext=%#p pPrev=%#p\n", pDisk->ListWriteGrowing.pNext,
2022 pDisk->ListWriteGrowing.pPrev));
2023
2024 RTCritSectLeave(&pDisk->CritSect);
2025
2026 /* Process the list. */
2027 do
2028 {
2029 PVDIOCTXDEFERRED pDeferred = RTListNodeGetFirst(&ListTmp, VDIOCTXDEFERRED, NodeDeferred);
2030 PVDIOCTX pIoCtxWait = pDeferred->pIoCtx;
2031
2032 AssertPtr(pIoCtxWait);
2033
2034 RTListNodeRemove(&pDeferred->NodeDeferred);
2035 RTMemFree(pDeferred);
2036
2037 Assert(!pIoCtxWait->pIoCtxParent);
2038
2039 pIoCtxWait->fBlocked = false;
2040 LogFlowFunc(("Processing waiting I/O context pIoCtxWait=%#p\n", pIoCtxWait));
2041
2042 rc = vdIoCtxProcess(pIoCtxWait);
2043 if ( rc == VINF_VD_ASYNC_IO_FINISHED
2044 && ASMAtomicCmpXchgBool(&pIoCtxWait->fComplete, true, false))
2045 {
2046 LogFlowFunc(("Waiting I/O context completed pIoCtxWait=%#p\n", pIoCtxWait));
2047 vdThreadFinishWrite(pDisk);
2048 pIoCtxWait->Type.Root.pfnComplete(pIoCtxWait->Type.Root.pvUser1,
2049 pIoCtxWait->Type.Root.pvUser2,
2050 pIoCtxWait->rcReq);
2051 vdIoCtxFree(pDisk, pIoCtxWait);
2052 }
2053 } while (!RTListIsEmpty(&ListTmp));
2054 }
2055 else
2056 RTCritSectLeave(&pDisk->CritSect);
2057 }
2058 else
2059 {
2060 if (pIoCtx->enmTxDir == VDIOCTXTXDIR_WRITE)
2061 vdThreadFinishWrite(pDisk);
2062 else
2063 vdThreadFinishRead(pDisk);
2064
2065 LogFlowFunc(("I/O context completed pIoCtx=%#p rcReq=%Rrc\n", pIoCtx, pIoCtx->rcReq));
2066 pIoCtx->Type.Root.pfnComplete(pIoCtx->Type.Root.pvUser1,
2067 pIoCtx->Type.Root.pvUser2,
2068 pIoCtx->rcReq);
2069 }
2070
2071 vdIoCtxFree(pDisk, pIoCtx);
2072 }
2073 }
2074
2075 return VINF_SUCCESS;
2076}
2077
2078/**
2079 * Internal - Called when user transfer completed.
2080 */
2081static int vdUserXferCompleted(PVDIOSTORAGE pIoStorage, PVDIOCTX pIoCtx,
2082 PFNVDXFERCOMPLETED pfnComplete, void *pvUser,
2083 size_t cbTransfer, int rcReq)
2084{
2085 int rc = VINF_SUCCESS;
2086 bool fIoCtxContinue = true;
2087 PVBOXHDD pDisk = pIoCtx->pDisk;
2088
2089 LogFlowFunc(("pIoStorage=%#p pIoCtx=%#p pfnComplete=%#p pvUser=%#p cbTransfer=%zu rcReq=%Rrc\n",
2090 pIoStorage, pIoCtx, pfnComplete, pvUser, cbTransfer, rcReq));
2091
2092 Assert(pIoCtx->cbTransferLeft >= cbTransfer);
2093 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbTransfer);
2094 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
2095
2096 if (pfnComplete)
2097 {
2098 RTCritSectEnter(&pDisk->CritSect);
2099 rc = pfnComplete(pIoStorage->pImage->pvBackendData, pIoCtx, pvUser, rcReq);
2100 RTCritSectLeave(&pDisk->CritSect);
2101 }
2102
2103 if (RT_SUCCESS(rc))
2104 rc = vdIoCtxContinue(pIoCtx, rcReq);
2105 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2106 rc = VINF_SUCCESS;
2107
2108 return rc;
2109}
2110
2111/**
2112 * Internal - Called when a meta transfer completed.
2113 */
2114static int vdMetaXferCompleted(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnComplete, void *pvUser,
2115 PVDMETAXFER pMetaXfer, int rcReq)
2116{
2117 PVBOXHDD pDisk = pIoStorage->pImage->pDisk;
2118 RTLISTNODE ListIoCtxWaiting;
2119 bool fFlush;
2120
2121 LogFlowFunc(("pIoStorage=%#p pfnComplete=%#p pvUser=%#p pMetaXfer=%#p rcReq=%Rrc\n",
2122 pIoStorage, pfnComplete, pvUser, pMetaXfer, rcReq));
2123
2124 RTCritSectEnter(&pDisk->CritSect);
2125 fFlush = VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_FLUSH;
2126 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
2127
2128 if (!fFlush)
2129 {
2130 RTListMove(&ListIoCtxWaiting, &pMetaXfer->ListIoCtxWaiting);
2131
2132 if (RT_FAILURE(rcReq))
2133 {
2134 /* Remove from the AVL tree. */
2135 LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
2136 bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key);
2137 Assert(fRemoved);
2138 RTMemFree(pMetaXfer);
2139 }
2140 else
2141 {
2142 /* Increase the reference counter to make sure it doesn't go away before the last context is processed. */
2143 pMetaXfer->cRefs++;
2144 }
2145 }
2146 else
2147 RTListMove(&ListIoCtxWaiting, &pMetaXfer->ListIoCtxWaiting);
2148 RTCritSectLeave(&pDisk->CritSect);
2149
2150 /* Go through the waiting list and continue the I/O contexts. */
2151 while (!RTListIsEmpty(&ListIoCtxWaiting))
2152 {
2153 int rc = VINF_SUCCESS;
2154 bool fContinue = true;
2155 PVDIOCTXDEFERRED pDeferred = RTListNodeGetFirst(&ListIoCtxWaiting, VDIOCTXDEFERRED, NodeDeferred);
2156 PVDIOCTX pIoCtx = pDeferred->pIoCtx;
2157 RTListNodeRemove(&pDeferred->NodeDeferred);
2158
2159 RTMemFree(pDeferred);
2160 ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
2161
2162 if (pfnComplete)
2163 {
2164 RTCritSectEnter(&pDisk->CritSect);
2165 rc = pfnComplete(pIoStorage->pImage->pvBackendData, pIoCtx, pvUser, rcReq);
2166 RTCritSectLeave(&pDisk->CritSect);
2167 }
2168
2169 LogFlow(("Completion callback for I/O context %#p returned %Rrc\n", pIoCtx, rc));
2170
2171 if (RT_SUCCESS(rc))
2172 {
2173 rc = vdIoCtxContinue(pIoCtx, rcReq);
2174 AssertRC(rc);
2175 }
2176 else
2177 Assert(rc == VERR_VD_ASYNC_IO_IN_PROGRESS);
2178 }
2179
2180 /* Remove if not used anymore. */
2181 if (RT_SUCCESS(rcReq) && !fFlush)
2182 {
2183 RTCritSectEnter(&pDisk->CritSect);
2184 pMetaXfer->cRefs--;
2185 if (!pMetaXfer->cRefs && RTListIsEmpty(&pMetaXfer->ListIoCtxWaiting))
2186 {
2187 /* Remove from the AVL tree. */
2188 LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
2189 bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key);
2190 Assert(fRemoved);
2191 RTMemFree(pMetaXfer);
2192 }
2193 RTCritSectLeave(&pDisk->CritSect);
2194 }
2195 else if (fFlush)
2196 RTMemFree(pMetaXfer);
2197
2198 return VINF_SUCCESS;
2199}
2200
2201static int vdIOReqCompleted(void *pvUser, int rcReq)
2202{
2203 int rc = VINF_SUCCESS;
2204 PVDIOTASK pIoTask = (PVDIOTASK)pvUser;
2205 PVDIOSTORAGE pIoStorage = pIoTask->pIoStorage;
2206
2207 LogFlowFunc(("Task completed pIoTask=%#p\n", pIoTask));
2208
2209 if (!pIoTask->fMeta)
2210 rc = vdUserXferCompleted(pIoStorage, pIoTask->Type.User.pIoCtx,
2211 pIoTask->pfnComplete, pIoTask->pvUser,
2212 pIoTask->Type.User.cbTransfer, rcReq);
2213 else
2214 rc = vdMetaXferCompleted(pIoStorage, pIoTask->pfnComplete, pIoTask->pvUser,
2215 pIoTask->Type.Meta.pMetaXfer, rcReq);
2216
2217 vdIoTaskFree(pIoStorage->pImage->pDisk, pIoTask);
2218
2219 return rc;
2220}
2221
2222/**
2223 * VD I/O interface callback for opening a file.
2224 */
2225static int vdIOOpen(void *pvUser, const char *pszLocation,
2226 unsigned uOpenFlags, PPVDIOSTORAGE ppIoStorage)
2227{
2228 int rc = VINF_SUCCESS;
2229 PVDIMAGE pImage = (PVDIMAGE)pvUser;
2230 PVBOXHDD pDisk = pImage->pDisk;
2231 PVDIOSTORAGE pIoStorage = (PVDIOSTORAGE)RTMemAllocZ(sizeof(VDIOSTORAGE));
2232
2233 if (!pIoStorage)
2234 return VERR_NO_MEMORY;
2235
2236 pIoStorage->pImage = pImage;
2237
2238 /* Create the AVl tree. */
2239 pIoStorage->pTreeMetaXfers = (PAVLRFOFFTREE)RTMemAllocZ(sizeof(AVLRFOFFTREE));
2240 if (pIoStorage->pTreeMetaXfers)
2241 {
2242 rc = pDisk->pInterfaceAsyncIOCallbacks->pfnOpen(pDisk->pInterfaceAsyncIO->pvUser,
2243 pszLocation, uOpenFlags,
2244 vdIOReqCompleted,
2245 pDisk->pVDIfsDisk,
2246 &pIoStorage->u.pStorage);
2247 if (RT_SUCCESS(rc))
2248 {
2249 *ppIoStorage = pIoStorage;
2250 return VINF_SUCCESS;
2251 }
2252
2253 RTMemFree(pIoStorage->pTreeMetaXfers);
2254 }
2255 else
2256 rc = VERR_NO_MEMORY;
2257
2258 RTMemFree(pIoStorage);
2259 return rc;
2260}
2261
2262static int vdIOTreeMetaXferDestroy(PAVLRFOFFNODECORE pNode, void *pvUser)
2263{
2264 AssertMsgFailed(("Tree should be empty at this point!\n"));
2265 return VINF_SUCCESS;
2266}
2267
2268static int vdIOClose(void *pvUser, PVDIOSTORAGE pIoStorage)
2269{
2270 PVDIMAGE pImage = (PVDIMAGE)pvUser;
2271 PVBOXHDD pDisk = pImage->pDisk;
2272
2273 int rc = pDisk->pInterfaceAsyncIOCallbacks->pfnClose(pDisk->pInterfaceAsyncIO->pvUser,
2274 pIoStorage->u.pStorage);
2275 AssertRC(rc);
2276
2277 RTAvlrFileOffsetDestroy(pIoStorage->pTreeMetaXfers, vdIOTreeMetaXferDestroy, NULL);
2278 RTMemFree(pIoStorage->pTreeMetaXfers);
2279 RTMemFree(pIoStorage);
2280 return VINF_SUCCESS;
2281}
2282
2283static int vdIOGetSize(void *pvUser, PVDIOSTORAGE pIoStorage,
2284 uint64_t *pcbSize)
2285{
2286 PVDIMAGE pImage = (PVDIMAGE)pvUser;
2287 PVBOXHDD pDisk = pImage->pDisk;
2288
2289 return pDisk->pInterfaceAsyncIOCallbacks->pfnGetSize(pDisk->pInterfaceAsyncIO->pvUser,
2290 pIoStorage->u.pStorage,
2291 pcbSize);
2292}
2293
2294static int vdIOSetSize(void *pvUser, PVDIOSTORAGE pIoStorage,
2295 uint64_t cbSize)
2296{
2297 PVDIMAGE pImage = (PVDIMAGE)pvUser;
2298 PVBOXHDD pDisk = pImage->pDisk;
2299
2300 return pDisk->pInterfaceAsyncIOCallbacks->pfnSetSize(pDisk->pInterfaceAsyncIO->pvUser,
2301 pIoStorage->u.pStorage,
2302 cbSize);
2303}
2304
2305static int vdIOWriteSync(void *pvUser, PVDIOSTORAGE pIoStorage, uint64_t uOffset,
2306 size_t cbWrite, const void *pvBuf, size_t *pcbWritten)
2307{
2308 PVDIMAGE pImage = (PVDIMAGE)pvUser;
2309 PVBOXHDD pDisk = pImage->pDisk;
2310
2311 return pDisk->pInterfaceAsyncIOCallbacks->pfnWriteSync(pDisk->pInterfaceAsyncIO->pvUser,
2312 pIoStorage->u.pStorage,
2313 uOffset, cbWrite, pvBuf,
2314 pcbWritten);
2315}
2316
2317static int vdIOReadSync(void *pvUser, PVDIOSTORAGE pIoStorage, uint64_t uOffset,
2318 size_t cbRead, void *pvBuf, size_t *pcbRead)
2319{
2320 PVDIMAGE pImage = (PVDIMAGE)pvUser;
2321 PVBOXHDD pDisk = pImage->pDisk;
2322
2323 return pDisk->pInterfaceAsyncIOCallbacks->pfnReadSync(pDisk->pInterfaceAsyncIO->pvUser,
2324 pIoStorage->u.pStorage,
2325 uOffset, cbRead, pvBuf,
2326 pcbRead);
2327}
2328
2329static int vdIOFlushSync(void *pvUser, PVDIOSTORAGE pIoStorage)
2330{
2331 PVDIMAGE pImage = (PVDIMAGE)pvUser;
2332 PVBOXHDD pDisk = pImage->pDisk;
2333
2334 return pDisk->pInterfaceAsyncIOCallbacks->pfnFlushSync(pDisk->pInterfaceAsyncIO->pvUser,
2335 pIoStorage->u.pStorage);
2336}
2337
2338static int vdIOReadUserAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
2339 uint64_t uOffset, PVDIOCTX pIoCtx,
2340 size_t cbRead)
2341{
2342 int rc = VINF_SUCCESS;
2343 PVDIMAGE pImage = (PVDIMAGE)pvUser;
2344 PVBOXHDD pDisk = pImage->pDisk;
2345
2346 LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pIoCtx=%#p cbRead=%u\n",
2347 pvUser, pIoStorage, uOffset, pIoCtx, cbRead));
2348
2349 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
2350
2351 /* Build the S/G array and spawn a new I/O task */
2352 while (cbRead)
2353 {
2354 RTSGSEG aSeg[VD_IO_TASK_SEGMENTS_MAX];
2355 unsigned cSegments = VD_IO_TASK_SEGMENTS_MAX;
2356 size_t cbTaskRead = 0;
2357
2358 cbTaskRead = RTSgBufSegArrayCreate(&pIoCtx->SgBuf, aSeg, &cSegments, cbRead);
2359
2360 AssertMsg(cbTaskRead <= cbRead, ("Invalid number of bytes to read\n"));
2361
2362 LogFlow(("Reading %u bytes into %u segments\n", cbTaskRead, cSegments));
2363
2364#ifdef RT_STRICT
2365 for (unsigned i = 0; i < cSegments; i++)
2366 AssertMsg(aSeg[i].pvSeg && !(aSeg[i].cbSeg % 512),
2367 ("Segment %u is invalid\n", i));
2368#endif
2369
2370 PVDIOTASK pIoTask = vdIoTaskUserAlloc(pIoStorage, NULL, NULL, pIoCtx, cbTaskRead);
2371
2372 if (!pIoTask)
2373 return VERR_NO_MEMORY;
2374
2375 ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
2376
2377 void *pvTask;
2378 rc = pDisk->pInterfaceAsyncIOCallbacks->pfnReadAsync(pDisk->pInterfaceAsyncIO->pvUser,
2379 pIoStorage->u.pStorage,
2380 uOffset, aSeg, cSegments,
2381 cbTaskRead, pIoTask,
2382 &pvTask);
2383 if (RT_SUCCESS(rc))
2384 {
2385 AssertMsg(cbTaskRead <= pIoCtx->cbTransferLeft, ("Impossible!\n"));
2386 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbTaskRead);
2387 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
2388 vdIoTaskFree(pDisk, pIoTask);
2389 }
2390 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
2391 {
2392 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
2393 break;
2394 }
2395
2396 uOffset += cbTaskRead;
2397 cbRead -= cbTaskRead;
2398 }
2399
2400 LogFlowFunc(("returns rc=%Rrc\n", rc));
2401 return rc;
2402}
2403
2404static int vdIOWriteUserAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
2405 uint64_t uOffset, PVDIOCTX pIoCtx,
2406 size_t cbWrite,
2407 PFNVDXFERCOMPLETED pfnComplete,
2408 void *pvCompleteUser)
2409{
2410 int rc = VINF_SUCCESS;
2411 PVDIMAGE pImage = (PVDIMAGE)pvUser;
2412 PVBOXHDD pDisk = pImage->pDisk;
2413
2414 LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pIoCtx=%#p cbWrite=%u\n",
2415 pvUser, pIoStorage, uOffset, pIoCtx, cbWrite));
2416
2417 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
2418
2419 /* Build the S/G array and spawn a new I/O task */
2420 while (cbWrite)
2421 {
2422 RTSGSEG aSeg[VD_IO_TASK_SEGMENTS_MAX];
2423 unsigned cSegments = VD_IO_TASK_SEGMENTS_MAX;
2424 size_t cbTaskWrite = 0;
2425
2426 cbTaskWrite = RTSgBufSegArrayCreate(&pIoCtx->SgBuf, aSeg, &cSegments, cbWrite);
2427
2428 AssertMsg(cbTaskWrite <= cbWrite, ("Invalid number of bytes to write\n"));
2429
2430 LogFlow(("Writing %u bytes from %u segments\n", cbTaskWrite, cSegments));
2431
2432#ifdef DEBUG
2433 for (unsigned i = 0; i < cSegments; i++)
2434 AssertMsg(aSeg[i].pvSeg && !(aSeg[i].cbSeg % 512),
2435 ("Segment %u is invalid\n", i));
2436#endif
2437
2438 PVDIOTASK pIoTask = vdIoTaskUserAlloc(pIoStorage, pfnComplete, pvCompleteUser, pIoCtx, cbTaskWrite);
2439
2440 if (!pIoTask)
2441 return VERR_NO_MEMORY;
2442
2443 ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
2444
2445 void *pvTask;
2446 rc = pDisk->pInterfaceAsyncIOCallbacks->pfnWriteAsync(pDisk->pInterfaceAsyncIO->pvUser,
2447 pIoStorage->u.pStorage,
2448 uOffset, aSeg, cSegments,
2449 cbTaskWrite, pIoTask,
2450 &pvTask);
2451 if (RT_SUCCESS(rc))
2452 {
2453 AssertMsg(cbTaskWrite <= pIoCtx->cbTransferLeft, ("Impossible!\n"));
2454 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbTaskWrite);
2455 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
2456 vdIoTaskFree(pDisk, pIoTask);
2457 }
2458 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
2459 {
2460 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
2461 break;
2462 }
2463
2464 uOffset += cbTaskWrite;
2465 cbWrite -= cbTaskWrite;
2466 }
2467
2468 return rc;
2469}
2470
2471static int vdIOReadMetaAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
2472 uint64_t uOffset, void *pvBuf,
2473 size_t cbRead, PVDIOCTX pIoCtx,
2474 PPVDMETAXFER ppMetaXfer,
2475 PFNVDXFERCOMPLETED pfnComplete,
2476 void *pvCompleteUser)
2477{
2478 PVDIMAGE pImage = (PVDIMAGE)pvUser;
2479 PVBOXHDD pDisk = pImage->pDisk;
2480 int rc = VINF_SUCCESS;
2481 RTSGSEG Seg;
2482 PVDIOTASK pIoTask;
2483 PVDMETAXFER pMetaXfer = NULL;
2484 void *pvTask = NULL;
2485
2486 LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pvBuf=%#p cbRead=%u\n",
2487 pvUser, pIoStorage, uOffset, pvBuf, cbRead));
2488
2489 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
2490
2491 pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGet(pIoStorage->pTreeMetaXfers, uOffset);
2492 if (!pMetaXfer)
2493 {
2494#ifdef RT_STRICT
2495 pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGetBestFit(pIoStorage->pTreeMetaXfers, uOffset, false /* fAbove */);
2496 AssertMsg(!pMetaXfer || (pMetaXfer->Core.Key + pMetaXfer->cbMeta <= uOffset),
2497 ("Overlapping meta transfers!\n"));
2498#endif
2499
2500 /* Allocate a new meta transfer. */
2501 pMetaXfer = vdMetaXferAlloc(pImage, pIoStorage, uOffset, cbRead);
2502 if (!pMetaXfer)
2503 return VERR_NO_MEMORY;
2504
2505 pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvCompleteUser, pMetaXfer);
2506 if (!pIoTask)
2507 {
2508 RTMemFree(pMetaXfer);
2509 return VERR_NO_MEMORY;
2510 }
2511
2512 Seg.cbSeg = cbRead;
2513 Seg.pvSeg = pMetaXfer->abData;
2514
2515 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_READ);
2516 rc = pDisk->pInterfaceAsyncIOCallbacks->pfnReadAsync(pDisk->pInterfaceAsyncIO->pvUser,
2517 pIoStorage->u.pStorage,
2518 uOffset, &Seg, 1,
2519 cbRead, pIoTask,
2520 &pvTask);
2521
2522 if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2523 {
2524 bool fInserted = RTAvlrFileOffsetInsert(pIoStorage->pTreeMetaXfers, &pMetaXfer->Core);
2525 Assert(fInserted);
2526 }
2527 else
2528 RTMemFree(pMetaXfer);
2529
2530 if (RT_SUCCESS(rc))
2531 {
2532 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
2533 vdIoTaskFree(pDisk, pIoTask);
2534 }
2535 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS && !pfnComplete)
2536 rc = VERR_VD_NOT_ENOUGH_METADATA;
2537 }
2538
2539 Assert(VALID_PTR(pMetaXfer) || RT_FAILURE(rc));
2540
2541 if (RT_SUCCESS(rc) || rc == VERR_VD_NOT_ENOUGH_METADATA || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2542 {
2543 /* If it is pending add the request to the list. */
2544 if (VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_READ)
2545 {
2546 PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
2547 AssertPtr(pDeferred);
2548
2549 RTListInit(&pDeferred->NodeDeferred);
2550 pDeferred->pIoCtx = pIoCtx;
2551
2552 ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
2553 RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
2554 rc = VERR_VD_NOT_ENOUGH_METADATA;
2555 }
2556 else
2557 {
2558 /* Transfer the data. */
2559 pMetaXfer->cRefs++;
2560 Assert(pMetaXfer->cbMeta >= cbRead);
2561 Assert(pMetaXfer->Core.Key == (RTFOFF)uOffset);
2562 memcpy(pvBuf, pMetaXfer->abData, cbRead);
2563 *ppMetaXfer = pMetaXfer;
2564 }
2565 }
2566
2567 return rc;
2568}
2569
2570static int vdIOWriteMetaAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
2571 uint64_t uOffset, void *pvBuf,
2572 size_t cbWrite, PVDIOCTX pIoCtx,
2573 PFNVDXFERCOMPLETED pfnComplete,
2574 void *pvCompleteUser)
2575{
2576 PVDIMAGE pImage = (PVDIMAGE)pvUser;
2577 PVBOXHDD pDisk = pImage->pDisk;
2578 int rc = VINF_SUCCESS;
2579 RTSGSEG Seg;
2580 PVDIOTASK pIoTask;
2581 PVDMETAXFER pMetaXfer = NULL;
2582 bool fInTree = false;
2583 void *pvTask = NULL;
2584
2585 LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pvBuf=%#p cbWrite=%u\n",
2586 pvUser, pIoStorage, uOffset, pvBuf, cbWrite));
2587
2588 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
2589
2590 pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGet(pIoStorage->pTreeMetaXfers, uOffset);
2591 if (!pMetaXfer)
2592 {
2593 /* Allocate a new meta transfer. */
2594 pMetaXfer = vdMetaXferAlloc(pImage, pIoStorage, uOffset, cbWrite);
2595 if (!pMetaXfer)
2596 return VERR_NO_MEMORY;
2597 }
2598 else
2599 {
2600 Assert(pMetaXfer->cbMeta >= cbWrite);
2601 Assert(pMetaXfer->Core.Key == (RTFOFF)uOffset);
2602 fInTree = true;
2603 }
2604
2605 Assert(VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE);
2606
2607 pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvCompleteUser, pMetaXfer);
2608 if (!pIoTask)
2609 {
2610 RTMemFree(pMetaXfer);
2611 return VERR_NO_MEMORY;
2612 }
2613
2614 memcpy(pMetaXfer->abData, pvBuf, cbWrite);
2615 Seg.cbSeg = cbWrite;
2616 Seg.pvSeg = pMetaXfer->abData;
2617
2618 ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
2619
2620 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_WRITE);
2621 rc = pDisk->pInterfaceAsyncIOCallbacks->pfnWriteAsync(pDisk->pInterfaceAsyncIO->pvUser,
2622 pIoStorage->u.pStorage,
2623 uOffset, &Seg, 1,
2624 cbWrite, pIoTask,
2625 &pvTask);
2626 if (RT_SUCCESS(rc))
2627 {
2628 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
2629 ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
2630 vdIoTaskFree(pDisk, pIoTask);
2631 if (fInTree && !pMetaXfer->cRefs)
2632 {
2633 LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
2634 bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key);
2635 AssertMsg(fRemoved, ("Metadata transfer wasn't removed\n"));
2636 RTMemFree(pMetaXfer);
2637 pMetaXfer = NULL;
2638 }
2639 }
2640 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2641 {
2642 PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
2643 AssertPtr(pDeferred);
2644
2645 RTListInit(&pDeferred->NodeDeferred);
2646 pDeferred->pIoCtx = pIoCtx;
2647
2648 if (!fInTree)
2649 {
2650 bool fInserted = RTAvlrFileOffsetInsert(pIoStorage->pTreeMetaXfers, &pMetaXfer->Core);
2651 Assert(fInserted);
2652 }
2653
2654 RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
2655 }
2656 else
2657 {
2658 RTMemFree(pMetaXfer);
2659 pMetaXfer = NULL;
2660 }
2661
2662 return rc;
2663}
2664
2665static void vdIOMetaXferRelease(void *pvUser, PVDMETAXFER pMetaXfer)
2666{
2667 PVDIMAGE pImage = (PVDIMAGE)pvUser;
2668 PVDIOSTORAGE pIoStorage = pMetaXfer->pIoStorage;
2669
2670 VD_THREAD_IS_CRITSECT_OWNER(pImage->pDisk);
2671
2672 Assert( VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE
2673 || VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_WRITE);
2674 Assert(pMetaXfer->cRefs > 0);
2675
2676 pMetaXfer->cRefs--;
2677 if ( !pMetaXfer->cRefs
2678 && RTListIsEmpty(&pMetaXfer->ListIoCtxWaiting)
2679 && VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE)
2680 {
2681 /* Free the meta data entry. */
2682 LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
2683 bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key);
2684 AssertMsg(fRemoved, ("Metadata transfer wasn't removed\n"));
2685
2686 RTMemFree(pMetaXfer);
2687 }
2688}
2689
2690static int vdIOFlushAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
2691 PVDIOCTX pIoCtx,
2692 PFNVDXFERCOMPLETED pfnComplete,
2693 void *pvCompleteUser)
2694{
2695 PVDIMAGE pImage = (PVDIMAGE)pvUser;
2696 PVBOXHDD pDisk = pImage->pDisk;
2697 int rc = VINF_SUCCESS;
2698 PVDIOTASK pIoTask;
2699 PVDMETAXFER pMetaXfer = NULL;
2700 void *pvTask = NULL;
2701
2702 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
2703
2704 LogFlowFunc(("pvUser=%#p pIoStorage=%#p pIoCtx=%#p\n",
2705 pvUser, pIoStorage, pIoCtx));
2706
2707 /* Allocate a new meta transfer. */
2708 pMetaXfer = vdMetaXferAlloc(pImage, pIoStorage, 0, 0);
2709 if (!pMetaXfer)
2710 return VERR_NO_MEMORY;
2711
2712 pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvUser, pMetaXfer);
2713 if (!pIoTask)
2714 {
2715 RTMemFree(pMetaXfer);
2716 return VERR_NO_MEMORY;
2717 }
2718
2719 ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
2720
2721 PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
2722 AssertPtr(pDeferred);
2723
2724 RTListInit(&pDeferred->NodeDeferred);
2725 pDeferred->pIoCtx = pIoCtx;
2726
2727 RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
2728 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_FLUSH);
2729 rc = pDisk->pInterfaceAsyncIOCallbacks->pfnFlushAsync(pDisk->pInterfaceAsyncIO->pvUser,
2730 pIoStorage->u.pStorage,
2731 pIoTask,
2732 &pvTask);
2733 if (RT_SUCCESS(rc))
2734 {
2735 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
2736 ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
2737 vdIoTaskFree(pDisk, pIoTask);
2738 RTMemFree(pDeferred);
2739 RTMemFree(pMetaXfer);
2740 }
2741 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
2742 RTMemFree(pMetaXfer);
2743
2744 return rc;
2745}
2746
2747static size_t vdIOIoCtxCopyTo(void *pvUser, PVDIOCTX pIoCtx,
2748 void *pvBuf, size_t cbBuf)
2749{
2750 PVDIMAGE pImage = (PVDIMAGE)pvUser;
2751 PVBOXHDD pDisk = pImage->pDisk;
2752
2753 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
2754
2755 return vdIoCtxCopyTo(pIoCtx, (uint8_t *)pvBuf, cbBuf);
2756}
2757
2758static size_t vdIOIoCtxCopyFrom(void *pvUser, PVDIOCTX pIoCtx,
2759 void *pvBuf, size_t cbBuf)
2760{
2761 PVDIMAGE pImage = (PVDIMAGE)pvUser;
2762 PVBOXHDD pDisk = pImage->pDisk;
2763
2764 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
2765
2766 return vdIoCtxCopyFrom(pIoCtx, (uint8_t *)pvBuf, cbBuf);
2767}
2768
2769static size_t vdIOIoCtxSet(void *pvUser, PVDIOCTX pIoCtx,
2770 int ch, size_t cb)
2771{
2772 PVDIMAGE pImage = (PVDIMAGE)pvUser;
2773 PVBOXHDD pDisk = pImage->pDisk;
2774
2775 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
2776
2777 return vdIoCtxSet(pIoCtx, ch, cb);
2778}
2779
2780/**
2781 * VD I/O interface callback for opening a file (limited version for VDGetFormat).
2782 */
2783static int vdIOOpenLimited(void *pvUser, const char *pszLocation,
2784 unsigned uOpenFlags, PPVDIOSTORAGE ppIoStorage)
2785{
2786 int rc = VINF_SUCCESS;
2787 PVDIOSTORAGE pIoStorage = (PVDIOSTORAGE)RTMemAllocZ(sizeof(VDIOSTORAGE));
2788
2789 if (!pIoStorage)
2790 return VERR_NO_MEMORY;
2791
2792 uint32_t fOpen = 0;
2793
2794 if (uOpenFlags & VD_INTERFACEASYNCIO_OPEN_FLAGS_READONLY)
2795 fOpen |= RTFILE_O_READ | RTFILE_O_DENY_NONE;
2796 else
2797 fOpen |= RTFILE_O_READWRITE | RTFILE_O_DENY_WRITE;
2798
2799 if (uOpenFlags & VD_INTERFACEASYNCIO_OPEN_FLAGS_CREATE)
2800 fOpen |= RTFILE_O_CREATE;
2801 else
2802 fOpen |= RTFILE_O_OPEN;
2803
2804 rc = RTFileOpen(&pIoStorage->u.hFile, pszLocation, fOpen);
2805 if (RT_SUCCESS(rc))
2806 *ppIoStorage = pIoStorage;
2807 else
2808 RTMemFree(pIoStorage);
2809
2810 return rc;
2811}
2812
2813static int vdIOCloseLimited(void *pvUser, PVDIOSTORAGE pIoStorage)
2814{
2815 int rc = RTFileClose(pIoStorage->u.hFile);
2816 AssertRC(rc);
2817
2818 RTMemFree(pIoStorage);
2819 return VINF_SUCCESS;
2820}
2821
2822static int vdIOGetSizeLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
2823 uint64_t *pcbSize)
2824{
2825 return RTFileGetSize(pIoStorage->u.hFile, pcbSize);
2826}
2827
2828static int vdIOSetSizeLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
2829 uint64_t cbSize)
2830{
2831 return RTFileSetSize(pIoStorage->u.hFile, cbSize);
2832}
2833
2834static int vdIOWriteSyncLimited(void *pvUser, PVDIOSTORAGE pIoStorage, uint64_t uOffset,
2835 size_t cbWrite, const void *pvBuf, size_t *pcbWritten)
2836{
2837 return RTFileWriteAt(pIoStorage->u.hFile, uOffset, pvBuf, cbWrite, pcbWritten);
2838}
2839
2840static int vdIOReadSyncLimited(void *pvUser, PVDIOSTORAGE pIoStorage, uint64_t uOffset,
2841 size_t cbRead, void *pvBuf, size_t *pcbRead)
2842{
2843 return RTFileReadAt(pIoStorage->u.hFile, uOffset, pvBuf, cbRead, pcbRead);
2844}
2845
2846static int vdIOFlushSyncLimited(void *pvUser, PVDIOSTORAGE pIoStorage)
2847{
2848 return RTFileFlush(pIoStorage->u.hFile);
2849}
2850
2851
2852/**
2853 * internal: send output to the log (unconditionally).
2854 */
2855int vdLogMessage(void *pvUser, const char *pszFormat, ...)
2856{
2857 NOREF(pvUser);
2858 va_list args;
2859 va_start(args, pszFormat);
2860 RTLogPrintf(pszFormat, args);
2861 va_end(args);
2862 return VINF_SUCCESS;
2863}
2864
2865
2866/**
2867 * Initializes HDD backends.
2868 *
2869 * @returns VBox status code.
2870 */
2871VBOXDDU_DECL(int) VDInit(void)
2872{
2873 int rc = vdAddBackends(aStaticBackends, RT_ELEMENTS(aStaticBackends));
2874 if (RT_SUCCESS(rc))
2875 rc = vdLoadDynamicBackends();
2876 LogRel(("VDInit finished\n"));
2877 return rc;
2878}
2879
2880/**
2881 * Destroys loaded HDD backends.
2882 *
2883 * @returns VBox status code.
2884 */
2885VBOXDDU_DECL(int) VDShutdown(void)
2886{
2887 PVBOXHDDBACKEND *pBackends = g_apBackends;
2888 unsigned cBackends = g_cBackends;
2889
2890 if (!pBackends)
2891 return VERR_INTERNAL_ERROR;
2892
2893 g_cBackends = 0;
2894 g_apBackends = NULL;
2895
2896 for (unsigned i = 0; i < cBackends; i++)
2897 if (pBackends[i]->hPlugin != NIL_RTLDRMOD)
2898 RTLdrClose(pBackends[i]->hPlugin);
2899
2900 RTMemFree(pBackends);
2901 return VINF_SUCCESS;
2902}
2903
2904
2905/**
2906 * Lists all HDD backends and their capabilities in a caller-provided buffer.
2907 *
2908 * @returns VBox status code.
2909 * VERR_BUFFER_OVERFLOW if not enough space is passed.
2910 * @param cEntriesAlloc Number of list entries available.
2911 * @param pEntries Pointer to array for the entries.
2912 * @param pcEntriesUsed Number of entries returned.
2913 */
2914VBOXDDU_DECL(int) VDBackendInfo(unsigned cEntriesAlloc, PVDBACKENDINFO pEntries,
2915 unsigned *pcEntriesUsed)
2916{
2917 int rc = VINF_SUCCESS;
2918 PRTDIR pPluginDir = NULL;
2919 unsigned cEntries = 0;
2920
2921 LogFlowFunc(("cEntriesAlloc=%u pEntries=%#p pcEntriesUsed=%#p\n", cEntriesAlloc, pEntries, pcEntriesUsed));
2922 /* Check arguments. */
2923 AssertMsgReturn(cEntriesAlloc,
2924 ("cEntriesAlloc=%u\n", cEntriesAlloc),
2925 VERR_INVALID_PARAMETER);
2926 AssertMsgReturn(VALID_PTR(pEntries),
2927 ("pEntries=%#p\n", pEntries),
2928 VERR_INVALID_PARAMETER);
2929 AssertMsgReturn(VALID_PTR(pcEntriesUsed),
2930 ("pcEntriesUsed=%#p\n", pcEntriesUsed),
2931 VERR_INVALID_PARAMETER);
2932 if (!g_apBackends)
2933 VDInit();
2934
2935 if (cEntriesAlloc < g_cBackends)
2936 {
2937 *pcEntriesUsed = g_cBackends;
2938 return VERR_BUFFER_OVERFLOW;
2939 }
2940
2941 for (unsigned i = 0; i < g_cBackends; i++)
2942 {
2943 pEntries[i].pszBackend = g_apBackends[i]->pszBackendName;
2944 pEntries[i].uBackendCaps = g_apBackends[i]->uBackendCaps;
2945 pEntries[i].papszFileExtensions = g_apBackends[i]->papszFileExtensions;
2946 pEntries[i].paConfigInfo = g_apBackends[i]->paConfigInfo;
2947 pEntries[i].pfnComposeLocation = g_apBackends[i]->pfnComposeLocation;
2948 pEntries[i].pfnComposeName = g_apBackends[i]->pfnComposeName;
2949 }
2950
2951 LogFlowFunc(("returns %Rrc *pcEntriesUsed=%u\n", rc, cEntries));
2952 *pcEntriesUsed = g_cBackends;
2953 return rc;
2954}
2955
2956/**
2957 * Lists the capablities of a backend indentified by its name.
2958 *
2959 * @returns VBox status code.
2960 * @param pszBackend The backend name.
2961 * @param pEntries Pointer to an entry.
2962 */
2963VBOXDDU_DECL(int) VDBackendInfoOne(const char *pszBackend, PVDBACKENDINFO pEntry)
2964{
2965 LogFlowFunc(("pszBackend=%#p pEntry=%#p\n", pszBackend, pEntry));
2966 /* Check arguments. */
2967 AssertMsgReturn(VALID_PTR(pszBackend),
2968 ("pszBackend=%#p\n", pszBackend),
2969 VERR_INVALID_PARAMETER);
2970 AssertMsgReturn(VALID_PTR(pEntry),
2971 ("pEntry=%#p\n", pEntry),
2972 VERR_INVALID_PARAMETER);
2973 if (!g_apBackends)
2974 VDInit();
2975
2976 /* Go through loaded backends. */
2977 for (unsigned i = 0; i < g_cBackends; i++)
2978 {
2979 if (!RTStrICmp(pszBackend, g_apBackends[i]->pszBackendName))
2980 {
2981 pEntry->pszBackend = g_apBackends[i]->pszBackendName;
2982 pEntry->uBackendCaps = g_apBackends[i]->uBackendCaps;
2983 pEntry->papszFileExtensions = g_apBackends[i]->papszFileExtensions;
2984 pEntry->paConfigInfo = g_apBackends[i]->paConfigInfo;
2985 return VINF_SUCCESS;
2986 }
2987 }
2988
2989 return VERR_NOT_FOUND;
2990}
2991
2992/**
2993 * Allocates and initializes an empty HDD container.
2994 * No image files are opened.
2995 *
2996 * @returns VBox status code.
2997 * @param pVDIfsDisk Pointer to the per-disk VD interface list.
2998 * @param ppDisk Where to store the reference to HDD container.
2999 */
3000VBOXDDU_DECL(int) VDCreate(PVDINTERFACE pVDIfsDisk, PVBOXHDD *ppDisk)
3001{
3002 int rc = VINF_SUCCESS;
3003 PVBOXHDD pDisk = NULL;
3004
3005 LogFlowFunc(("pVDIfsDisk=%#p\n", pVDIfsDisk));
3006 do
3007 {
3008 /* Check arguments. */
3009 AssertMsgBreakStmt(VALID_PTR(ppDisk),
3010 ("ppDisk=%#p\n", ppDisk),
3011 rc = VERR_INVALID_PARAMETER);
3012
3013 pDisk = (PVBOXHDD)RTMemAllocZ(sizeof(VBOXHDD));
3014 if (pDisk)
3015 {
3016 pDisk->u32Signature = VBOXHDDDISK_SIGNATURE;
3017 pDisk->cImages = 0;
3018 pDisk->pBase = NULL;
3019 pDisk->pLast = NULL;
3020 pDisk->cbSize = 0;
3021 pDisk->PCHSGeometry.cCylinders = 0;
3022 pDisk->PCHSGeometry.cHeads = 0;
3023 pDisk->PCHSGeometry.cSectors = 0;
3024 pDisk->LCHSGeometry.cCylinders = 0;
3025 pDisk->LCHSGeometry.cHeads = 0;
3026 pDisk->LCHSGeometry.cSectors = 0;
3027 pDisk->pVDIfsDisk = pVDIfsDisk;
3028 pDisk->pInterfaceError = NULL;
3029 pDisk->pInterfaceErrorCallbacks = NULL;
3030 pDisk->pInterfaceThreadSync = NULL;
3031 pDisk->pInterfaceThreadSyncCallbacks = NULL;
3032 pDisk->fGrowing = false;
3033 RTListInit(&pDisk->ListWriteGrowing);
3034
3035 /* Create the I/O ctx cache */
3036 rc = RTMemCacheCreate(&pDisk->hMemCacheIoCtx, sizeof(VDIOCTX), 0, UINT32_MAX,
3037 NULL, NULL, NULL, 0);
3038 if (RT_FAILURE(rc))
3039 {
3040 RTMemFree(pDisk);
3041 break;
3042 }
3043
3044 /* Create the I/O task cache */
3045 rc = RTMemCacheCreate(&pDisk->hMemCacheIoTask, sizeof(VDIOTASK), 0, UINT32_MAX,
3046 NULL, NULL, NULL, 0);
3047 if (RT_FAILURE(rc))
3048 {
3049 RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
3050 RTMemFree(pDisk);
3051 break;
3052 }
3053
3054 /* Create critical section. */
3055 rc = RTCritSectInit(&pDisk->CritSect);
3056 if (RT_FAILURE(rc))
3057 {
3058 RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
3059 RTMemCacheDestroy(pDisk->hMemCacheIoTask);
3060 RTMemFree(pDisk);
3061 break;
3062 }
3063
3064 pDisk->pInterfaceError = VDInterfaceGet(pVDIfsDisk, VDINTERFACETYPE_ERROR);
3065 if (pDisk->pInterfaceError)
3066 pDisk->pInterfaceErrorCallbacks = VDGetInterfaceError(pDisk->pInterfaceError);
3067
3068 pDisk->pInterfaceThreadSync = VDInterfaceGet(pVDIfsDisk, VDINTERFACETYPE_THREADSYNC);
3069 if (pDisk->pInterfaceThreadSync)
3070 pDisk->pInterfaceThreadSyncCallbacks = VDGetInterfaceThreadSync(pDisk->pInterfaceThreadSync);
3071 pDisk->pInterfaceAsyncIO = VDInterfaceGet(pVDIfsDisk, VDINTERFACETYPE_ASYNCIO);
3072 if (pDisk->pInterfaceAsyncIO)
3073 pDisk->pInterfaceAsyncIOCallbacks = VDGetInterfaceAsyncIO(pDisk->pInterfaceAsyncIO);
3074 else
3075 {
3076 /* Create fallback async I/O interface */
3077 pDisk->VDIAsyncIOCallbacks.cbSize = sizeof(VDINTERFACEASYNCIO);
3078 pDisk->VDIAsyncIOCallbacks.enmInterface = VDINTERFACETYPE_ASYNCIO;
3079 pDisk->VDIAsyncIOCallbacks.pfnOpen = vdAsyncIOOpen;
3080 pDisk->VDIAsyncIOCallbacks.pfnClose = vdAsyncIOClose;
3081 pDisk->VDIAsyncIOCallbacks.pfnGetSize = vdAsyncIOGetSize;
3082 pDisk->VDIAsyncIOCallbacks.pfnSetSize = vdAsyncIOSetSize;
3083 pDisk->VDIAsyncIOCallbacks.pfnReadSync = vdAsyncIOReadSync;
3084 pDisk->VDIAsyncIOCallbacks.pfnWriteSync = vdAsyncIOWriteSync;
3085 pDisk->VDIAsyncIOCallbacks.pfnFlushSync = vdAsyncIOFlushSync;
3086 pDisk->VDIAsyncIOCallbacks.pfnReadAsync = vdAsyncIOReadAsync;
3087 pDisk->VDIAsyncIOCallbacks.pfnWriteAsync = vdAsyncIOWriteAsync;
3088 pDisk->VDIAsyncIOCallbacks.pfnFlushAsync = vdAsyncIOFlushAsync;
3089 pDisk->pInterfaceAsyncIOCallbacks = &pDisk->VDIAsyncIOCallbacks;
3090
3091 pDisk->VDIAsyncIO.pszInterfaceName = "VD_AsyncIO";
3092 pDisk->VDIAsyncIO.cbSize = sizeof(VDINTERFACE);
3093 pDisk->VDIAsyncIO.pNext = NULL;
3094 pDisk->VDIAsyncIO.enmInterface = VDINTERFACETYPE_ASYNCIO;
3095 pDisk->VDIAsyncIO.pvUser = pDisk;
3096 pDisk->VDIAsyncIO.pCallbacks = pDisk->pInterfaceAsyncIOCallbacks;
3097 pDisk->pInterfaceAsyncIO = &pDisk->VDIAsyncIO;
3098 }
3099
3100 /* Create the I/O callback table. */
3101 pDisk->VDIIOCallbacks.cbSize = sizeof(VDINTERFACEIO);
3102 pDisk->VDIIOCallbacks.enmInterface = VDINTERFACETYPE_IO;
3103 pDisk->VDIIOCallbacks.pfnOpen = vdIOOpen;
3104 pDisk->VDIIOCallbacks.pfnClose = vdIOClose;
3105 pDisk->VDIIOCallbacks.pfnGetSize = vdIOGetSize;
3106 pDisk->VDIIOCallbacks.pfnSetSize = vdIOSetSize;
3107 pDisk->VDIIOCallbacks.pfnReadSync = vdIOReadSync;
3108 pDisk->VDIIOCallbacks.pfnWriteSync = vdIOWriteSync;
3109 pDisk->VDIIOCallbacks.pfnFlushSync = vdIOFlushSync;
3110 pDisk->VDIIOCallbacks.pfnReadUserAsync = vdIOReadUserAsync;
3111 pDisk->VDIIOCallbacks.pfnWriteUserAsync = vdIOWriteUserAsync;
3112 pDisk->VDIIOCallbacks.pfnReadMetaAsync = vdIOReadMetaAsync;
3113 pDisk->VDIIOCallbacks.pfnWriteMetaAsync = vdIOWriteMetaAsync;
3114 pDisk->VDIIOCallbacks.pfnMetaXferRelease = vdIOMetaXferRelease;
3115 pDisk->VDIIOCallbacks.pfnFlushAsync = vdIOFlushAsync;
3116 pDisk->VDIIOCallbacks.pfnIoCtxCopyFrom = vdIOIoCtxCopyFrom;
3117 pDisk->VDIIOCallbacks.pfnIoCtxCopyTo = vdIOIoCtxCopyTo;
3118 pDisk->VDIIOCallbacks.pfnIoCtxSet = vdIOIoCtxSet;
3119
3120 *ppDisk = pDisk;
3121 }
3122 else
3123 {
3124 rc = VERR_NO_MEMORY;
3125 break;
3126 }
3127 } while (0);
3128
3129 LogFlowFunc(("returns %Rrc (pDisk=%#p)\n", rc, pDisk));
3130 return rc;
3131}
3132
3133/**
3134 * Destroys HDD container.
3135 * If container has opened image files they will be closed.
3136 *
3137 * @param pDisk Pointer to HDD container.
3138 */
3139VBOXDDU_DECL(void) VDDestroy(PVBOXHDD pDisk)
3140{
3141 LogFlowFunc(("pDisk=%#p\n", pDisk));
3142 do
3143 {
3144 /* sanity check */
3145 AssertPtrBreak(pDisk);
3146 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3147 VDCloseAll(pDisk);
3148 RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
3149 RTMemCacheDestroy(pDisk->hMemCacheIoTask);
3150 RTMemFree(pDisk);
3151 } while (0);
3152 LogFlowFunc(("returns\n"));
3153}
3154
3155/**
3156 * Try to get the backend name which can use this image.
3157 *
3158 * @returns VBox status code.
3159 * VINF_SUCCESS if a plugin was found.
3160 * ppszFormat contains the string which can be used as backend name.
3161 * VERR_NOT_SUPPORTED if no backend was found.
3162 * @param pVDIfsDisk Pointer to the per-disk VD interface list.
3163 * @param pszFilename Name of the image file for which the backend is queried.
3164 * @param ppszFormat Receives pointer of the UTF-8 string which contains the format name.
3165 * The returned pointer must be freed using RTStrFree().
3166 */
3167VBOXDDU_DECL(int) VDGetFormat(PVDINTERFACE pVDIfsDisk, const char *pszFilename, char **ppszFormat)
3168{
3169 int rc = VERR_NOT_SUPPORTED;
3170 VDINTERFACEIO VDIIOCallbacks;
3171 VDINTERFACE VDIIO;
3172
3173 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
3174 /* Check arguments. */
3175 AssertMsgReturn(VALID_PTR(pszFilename) && *pszFilename,
3176 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
3177 VERR_INVALID_PARAMETER);
3178 AssertMsgReturn(VALID_PTR(ppszFormat),
3179 ("ppszFormat=%#p\n", ppszFormat),
3180 VERR_INVALID_PARAMETER);
3181
3182 if (!g_apBackends)
3183 VDInit();
3184
3185 VDIIOCallbacks.cbSize = sizeof(VDINTERFACEIO);
3186 VDIIOCallbacks.enmInterface = VDINTERFACETYPE_IO;
3187 VDIIOCallbacks.pfnOpen = vdIOOpenLimited;
3188 VDIIOCallbacks.pfnClose = vdIOCloseLimited;
3189 VDIIOCallbacks.pfnGetSize = vdIOGetSizeLimited;
3190 VDIIOCallbacks.pfnSetSize = vdIOSetSizeLimited;
3191 VDIIOCallbacks.pfnReadSync = vdIOReadSyncLimited;
3192 VDIIOCallbacks.pfnWriteSync = vdIOWriteSyncLimited;
3193 VDIIOCallbacks.pfnFlushSync = vdIOFlushSyncLimited;
3194 VDIIOCallbacks.pfnReadUserAsync = NULL;
3195 VDIIOCallbacks.pfnWriteUserAsync = NULL;
3196 VDIIOCallbacks.pfnReadMetaAsync = NULL;
3197 VDIIOCallbacks.pfnWriteMetaAsync = NULL;
3198 VDIIOCallbacks.pfnFlushAsync = NULL;
3199 rc = VDInterfaceAdd(&VDIIO, "VD_IO", VDINTERFACETYPE_IO,
3200 &VDIIOCallbacks, NULL, &pVDIfsDisk);
3201 AssertRC(rc);
3202
3203 /* Find the backend supporting this file format. */
3204 for (unsigned i = 0; i < g_cBackends; i++)
3205 {
3206 if (g_apBackends[i]->pfnCheckIfValid)
3207 {
3208 rc = g_apBackends[i]->pfnCheckIfValid(pszFilename, pVDIfsDisk);
3209 if ( RT_SUCCESS(rc)
3210 /* The correct backend has been found, but there is a small
3211 * incompatibility so that the file cannot be used. Stop here
3212 * and signal success - the actual open will of course fail,
3213 * but that will create a really sensible error message. */
3214 || ( rc != VERR_VD_GEN_INVALID_HEADER
3215 && rc != VERR_VD_VDI_INVALID_HEADER
3216 && rc != VERR_VD_VMDK_INVALID_HEADER
3217 && rc != VERR_VD_ISCSI_INVALID_HEADER
3218 && rc != VERR_VD_VHD_INVALID_HEADER
3219 && rc != VERR_VD_RAW_INVALID_HEADER))
3220 {
3221 /* Copy the name into the new string. */
3222 char *pszFormat = RTStrDup(g_apBackends[i]->pszBackendName);
3223 if (!pszFormat)
3224 {
3225 rc = VERR_NO_MEMORY;
3226 break;
3227 }
3228 *ppszFormat = pszFormat;
3229 rc = VINF_SUCCESS;
3230 break;
3231 }
3232 rc = VERR_NOT_SUPPORTED;
3233 }
3234 }
3235
3236 LogFlowFunc(("returns %Rrc *ppszFormat=\"%s\"\n", rc, *ppszFormat));
3237 return rc;
3238}
3239
3240/**
3241 * Opens an image file.
3242 *
3243 * The first opened image file in HDD container must have a base image type,
3244 * others (next opened images) must be a differencing or undo images.
3245 * Linkage is checked for differencing image to be in consistence with the previously opened image.
3246 * When another differencing image is opened and the last image was opened in read/write access
3247 * mode, then the last image is reopened in read-only with deny write sharing mode. This allows
3248 * other processes to use images in read-only mode too.
3249 *
3250 * Note that the image is opened in read-only mode if a read/write open is not possible.
3251 * Use VDIsReadOnly to check open mode.
3252 *
3253 * @returns VBox status code.
3254 * @param pDisk Pointer to HDD container.
3255 * @param pszBackend Name of the image file backend to use.
3256 * @param pszFilename Name of the image file to open.
3257 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
3258 * @param pVDIfsImage Pointer to the per-image VD interface list.
3259 */
3260VBOXDDU_DECL(int) VDOpen(PVBOXHDD pDisk, const char *pszBackend,
3261 const char *pszFilename, unsigned uOpenFlags,
3262 PVDINTERFACE pVDIfsImage)
3263{
3264 int rc = VINF_SUCCESS;
3265 int rc2;
3266 bool fLockWrite = false;
3267 PVDIMAGE pImage = NULL;
3268
3269 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x, pVDIfsImage=%#p\n",
3270 pDisk, pszBackend, pszFilename, uOpenFlags, pVDIfsImage));
3271
3272 do
3273 {
3274 /* sanity check */
3275 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3276 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3277
3278 /* Check arguments. */
3279 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
3280 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
3281 rc = VERR_INVALID_PARAMETER);
3282 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
3283 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
3284 rc = VERR_INVALID_PARAMETER);
3285 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
3286 ("uOpenFlags=%#x\n", uOpenFlags),
3287 rc = VERR_INVALID_PARAMETER);
3288
3289 /* Set up image descriptor. */
3290 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
3291 if (!pImage)
3292 {
3293 rc = VERR_NO_MEMORY;
3294 break;
3295 }
3296 pImage->pszFilename = RTStrDup(pszFilename);
3297 if (!pImage->pszFilename)
3298 {
3299 rc = VERR_NO_MEMORY;
3300 break;
3301 }
3302
3303 pImage->pDisk = pDisk;
3304 pImage->pVDIfsImage = pVDIfsImage;
3305
3306 rc = vdFindBackend(pszBackend, &pImage->Backend);
3307 if (RT_FAILURE(rc))
3308 break;
3309 if (!pImage->Backend)
3310 {
3311 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
3312 N_("VD: unknown backend name '%s'"), pszBackend);
3313 break;
3314 }
3315
3316 /* Set up the I/O interface. */
3317 rc = VDInterfaceAdd(&pImage->VDIIO, "VD_IO", VDINTERFACETYPE_IO,
3318 &pDisk->VDIIOCallbacks, pImage, &pImage->pVDIfsImage);
3319 AssertRC(rc);
3320
3321 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
3322 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
3323 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
3324 pDisk->pVDIfsDisk,
3325 pImage->pVDIfsImage,
3326 &pImage->pvBackendData);
3327 /* If the open in read-write mode failed, retry in read-only mode. */
3328 if (RT_FAILURE(rc))
3329 {
3330 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
3331 && ( rc == VERR_ACCESS_DENIED
3332 || rc == VERR_PERMISSION_DENIED
3333 || rc == VERR_WRITE_PROTECT
3334 || rc == VERR_SHARING_VIOLATION
3335 || rc == VERR_FILE_LOCK_FAILED))
3336 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
3337 (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
3338 | VD_OPEN_FLAGS_READONLY,
3339 pDisk->pVDIfsDisk,
3340 pImage->pVDIfsImage,
3341 &pImage->pvBackendData);
3342 if (RT_FAILURE(rc))
3343 {
3344 rc = vdError(pDisk, rc, RT_SRC_POS,
3345 N_("VD: error %Rrc opening image file '%s'"), rc, pszFilename);
3346 break;
3347 }
3348 }
3349
3350 /* Lock disk for writing, as we modify pDisk information below. */
3351 rc2 = vdThreadStartWrite(pDisk);
3352 AssertRC(rc2);
3353 fLockWrite = true;
3354
3355 /* Check image type. As the image itself has only partial knowledge
3356 * whether it's a base image or not, this info is derived here. The
3357 * base image can be fixed or normal, all others must be normal or
3358 * diff images. Some image formats don't distinguish between normal
3359 * and diff images, so this must be corrected here. */
3360 unsigned uImageFlags;
3361 uImageFlags = pImage->Backend->pfnGetImageFlags(pImage->pvBackendData);
3362 if (RT_FAILURE(rc))
3363 uImageFlags = VD_IMAGE_FLAGS_NONE;
3364 if ( RT_SUCCESS(rc)
3365 && !(uOpenFlags & VD_OPEN_FLAGS_INFO))
3366 {
3367 if ( pDisk->cImages == 0
3368 && (uImageFlags & VD_IMAGE_FLAGS_DIFF))
3369 {
3370 rc = VERR_VD_INVALID_TYPE;
3371 break;
3372 }
3373 else if (pDisk->cImages != 0)
3374 {
3375 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
3376 {
3377 rc = VERR_VD_INVALID_TYPE;
3378 break;
3379 }
3380 else
3381 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
3382 }
3383 }
3384
3385 /* Ensure we always get correct diff information, even if the backend
3386 * doesn't actually have a stored flag for this. It must not return
3387 * bogus information for the parent UUID if it is not a diff image. */
3388 RTUUID parentUuid;
3389 RTUuidClear(&parentUuid);
3390 rc2 = pImage->Backend->pfnGetParentUuid(pImage->pvBackendData, &parentUuid);
3391 if (RT_SUCCESS(rc2) && !RTUuidIsNull(&parentUuid))
3392 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
3393
3394 pImage->uImageFlags = uImageFlags;
3395
3396 /* Force sane optimization settings. It's not worth avoiding writes
3397 * to fixed size images. The overhead would have almost no payback. */
3398 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
3399 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
3400
3401 /** @todo optionally check UUIDs */
3402
3403 /* Cache disk information. */
3404 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
3405
3406 /* Cache PCHS geometry. */
3407 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
3408 &pDisk->PCHSGeometry);
3409 if (RT_FAILURE(rc2))
3410 {
3411 pDisk->PCHSGeometry.cCylinders = 0;
3412 pDisk->PCHSGeometry.cHeads = 0;
3413 pDisk->PCHSGeometry.cSectors = 0;
3414 }
3415 else
3416 {
3417 /* Make sure the PCHS geometry is properly clipped. */
3418 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
3419 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
3420 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
3421 }
3422
3423 /* Cache LCHS geometry. */
3424 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
3425 &pDisk->LCHSGeometry);
3426 if (RT_FAILURE(rc2))
3427 {
3428 pDisk->LCHSGeometry.cCylinders = 0;
3429 pDisk->LCHSGeometry.cHeads = 0;
3430 pDisk->LCHSGeometry.cSectors = 0;
3431 }
3432 else
3433 {
3434 /* Make sure the LCHS geometry is properly clipped. */
3435 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
3436 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
3437 }
3438
3439 if (pDisk->cImages != 0)
3440 {
3441 /* Switch previous image to read-only mode. */
3442 unsigned uOpenFlagsPrevImg;
3443 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
3444 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
3445 {
3446 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
3447 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData, uOpenFlagsPrevImg);
3448 }
3449 }
3450
3451 if (RT_SUCCESS(rc))
3452 {
3453 /* Image successfully opened, make it the last image. */
3454 vdAddImageToList(pDisk, pImage);
3455 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
3456 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
3457 }
3458 else
3459 {
3460 /* Error detected, but image opened. Close image. */
3461 rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, false);
3462 AssertRC(rc2);
3463 pImage->pvBackendData = NULL;
3464 }
3465 } while (0);
3466
3467 if (RT_UNLIKELY(fLockWrite))
3468 {
3469 rc2 = vdThreadFinishWrite(pDisk);
3470 AssertRC(rc2);
3471 }
3472
3473 if (RT_FAILURE(rc))
3474 {
3475 if (pImage)
3476 {
3477 if (pImage->pszFilename)
3478 RTStrFree(pImage->pszFilename);
3479 RTMemFree(pImage);
3480 }
3481 }
3482
3483 LogFlowFunc(("returns %Rrc\n", rc));
3484 return rc;
3485}
3486
3487/**
3488 * Creates and opens a new base image file.
3489 *
3490 * @returns VBox status code.
3491 * @param pDisk Pointer to HDD container.
3492 * @param pszBackend Name of the image file backend to use.
3493 * @param pszFilename Name of the image file to create.
3494 * @param cbSize Image size in bytes.
3495 * @param uImageFlags Flags specifying special image features.
3496 * @param pszComment Pointer to image comment. NULL is ok.
3497 * @param pPCHSGeometry Pointer to physical disk geometry <= (16383,16,63). Not NULL.
3498 * @param pLCHSGeometry Pointer to logical disk geometry <= (x,255,63). Not NULL.
3499 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
3500 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
3501 * @param pVDIfsImage Pointer to the per-image VD interface list.
3502 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
3503 */
3504VBOXDDU_DECL(int) VDCreateBase(PVBOXHDD pDisk, const char *pszBackend,
3505 const char *pszFilename, uint64_t cbSize,
3506 unsigned uImageFlags, const char *pszComment,
3507 PCPDMMEDIAGEOMETRY pPCHSGeometry,
3508 PCPDMMEDIAGEOMETRY pLCHSGeometry,
3509 PCRTUUID pUuid, unsigned uOpenFlags,
3510 PVDINTERFACE pVDIfsImage,
3511 PVDINTERFACE pVDIfsOperation)
3512{
3513 int rc = VINF_SUCCESS;
3514 int rc2;
3515 bool fLockWrite = false, fLockRead = false;
3516 PVDIMAGE pImage = NULL;
3517 RTUUID uuid;
3518
3519 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",
3520 pDisk, pszBackend, pszFilename, cbSize, uImageFlags, pszComment,
3521 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
3522 pPCHSGeometry->cSectors, pLCHSGeometry->cCylinders,
3523 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors, pUuid,
3524 uOpenFlags, pVDIfsImage, pVDIfsOperation));
3525
3526 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
3527 VDINTERFACETYPE_PROGRESS);
3528 PVDINTERFACEPROGRESS pCbProgress = NULL;
3529 if (pIfProgress)
3530 pCbProgress = VDGetInterfaceProgress(pIfProgress);
3531
3532 do
3533 {
3534 /* sanity check */
3535 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3536 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3537
3538 /* Check arguments. */
3539 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
3540 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
3541 rc = VERR_INVALID_PARAMETER);
3542 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
3543 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
3544 rc = VERR_INVALID_PARAMETER);
3545 AssertMsgBreakStmt(cbSize,
3546 ("cbSize=%llu\n", cbSize),
3547 rc = VERR_INVALID_PARAMETER);
3548 AssertMsgBreakStmt( ((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0)
3549 || ((uImageFlags & (VD_IMAGE_FLAGS_FIXED | VD_IMAGE_FLAGS_DIFF)) != VD_IMAGE_FLAGS_FIXED),
3550 ("uImageFlags=%#x\n", uImageFlags),
3551 rc = VERR_INVALID_PARAMETER);
3552 /* The PCHS geometry fields may be 0 to leave it for later. */
3553 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
3554 && pPCHSGeometry->cHeads <= 16
3555 && pPCHSGeometry->cSectors <= 63,
3556 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
3557 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
3558 pPCHSGeometry->cSectors),
3559 rc = VERR_INVALID_PARAMETER);
3560 /* The LCHS geometry fields may be 0 to leave it to later autodetection. */
3561 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
3562 && pLCHSGeometry->cHeads <= 255
3563 && pLCHSGeometry->cSectors <= 63,
3564 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
3565 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
3566 pLCHSGeometry->cSectors),
3567 rc = VERR_INVALID_PARAMETER);
3568 /* The UUID may be NULL. */
3569 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
3570 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
3571 rc = VERR_INVALID_PARAMETER);
3572 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
3573 ("uOpenFlags=%#x\n", uOpenFlags),
3574 rc = VERR_INVALID_PARAMETER);
3575
3576 /* Check state. Needs a temporary read lock. Holding the write lock
3577 * all the time would be blocking other activities for too long. */
3578 rc2 = vdThreadStartRead(pDisk);
3579 AssertRC(rc2);
3580 fLockRead = true;
3581 AssertMsgBreakStmt(pDisk->cImages == 0,
3582 ("Create base image cannot be done with other images open\n"),
3583 rc = VERR_VD_INVALID_STATE);
3584 rc2 = vdThreadFinishRead(pDisk);
3585 AssertRC(rc2);
3586 fLockRead = false;
3587
3588 /* Set up image descriptor. */
3589 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
3590 if (!pImage)
3591 {
3592 rc = VERR_NO_MEMORY;
3593 break;
3594 }
3595 pImage->pszFilename = RTStrDup(pszFilename);
3596 if (!pImage->pszFilename)
3597 {
3598 rc = VERR_NO_MEMORY;
3599 break;
3600 }
3601 pImage->pDisk = pDisk;
3602 pImage->pVDIfsImage = pVDIfsImage;
3603
3604 /* Set up the I/O interface. */
3605 rc = VDInterfaceAdd(&pImage->VDIIO, "VD_IO", VDINTERFACETYPE_IO,
3606 &pDisk->VDIIOCallbacks, pImage, &pImage->pVDIfsImage);
3607 AssertRC(rc);
3608
3609 rc = vdFindBackend(pszBackend, &pImage->Backend);
3610 if (RT_FAILURE(rc))
3611 break;
3612 if (!pImage->Backend)
3613 {
3614 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
3615 N_("VD: unknown backend name '%s'"), pszBackend);
3616 break;
3617 }
3618
3619 /* Create UUID if the caller didn't specify one. */
3620 if (!pUuid)
3621 {
3622 rc = RTUuidCreate(&uuid);
3623 if (RT_FAILURE(rc))
3624 {
3625 rc = vdError(pDisk, rc, RT_SRC_POS,
3626 N_("VD: cannot generate UUID for image '%s'"),
3627 pszFilename);
3628 break;
3629 }
3630 pUuid = &uuid;
3631 }
3632
3633 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
3634 uImageFlags &= ~VD_IMAGE_FLAGS_DIFF;
3635 rc = pImage->Backend->pfnCreate(pImage->pszFilename, cbSize,
3636 uImageFlags, pszComment, pPCHSGeometry,
3637 pLCHSGeometry, pUuid,
3638 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
3639 0, 99,
3640 pDisk->pVDIfsDisk,
3641 pImage->pVDIfsImage,
3642 pVDIfsOperation,
3643 &pImage->pvBackendData);
3644
3645 if (RT_SUCCESS(rc))
3646 {
3647 pImage->uImageFlags = uImageFlags;
3648
3649 /* Force sane optimization settings. It's not worth avoiding writes
3650 * to fixed size images. The overhead would have almost no payback. */
3651 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
3652 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
3653
3654 /* Lock disk for writing, as we modify pDisk information below. */
3655 rc2 = vdThreadStartWrite(pDisk);
3656 AssertRC(rc2);
3657 fLockWrite = true;
3658
3659 /** @todo optionally check UUIDs */
3660
3661 /* Re-check state, as the lock wasn't held and another image
3662 * creation call could have been done by another thread. */
3663 AssertMsgStmt(pDisk->cImages == 0,
3664 ("Create base image cannot be done with other images open\n"),
3665 rc = VERR_VD_INVALID_STATE);
3666 }
3667
3668 if (RT_SUCCESS(rc))
3669 {
3670 /* Cache disk information. */
3671 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
3672
3673 /* Cache PCHS geometry. */
3674 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
3675 &pDisk->PCHSGeometry);
3676 if (RT_FAILURE(rc2))
3677 {
3678 pDisk->PCHSGeometry.cCylinders = 0;
3679 pDisk->PCHSGeometry.cHeads = 0;
3680 pDisk->PCHSGeometry.cSectors = 0;
3681 }
3682 else
3683 {
3684 /* Make sure the CHS geometry is properly clipped. */
3685 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
3686 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
3687 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
3688 }
3689
3690 /* Cache LCHS geometry. */
3691 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
3692 &pDisk->LCHSGeometry);
3693 if (RT_FAILURE(rc2))
3694 {
3695 pDisk->LCHSGeometry.cCylinders = 0;
3696 pDisk->LCHSGeometry.cHeads = 0;
3697 pDisk->LCHSGeometry.cSectors = 0;
3698 }
3699 else
3700 {
3701 /* Make sure the CHS geometry is properly clipped. */
3702 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
3703 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
3704 }
3705
3706 /* Image successfully opened, make it the last image. */
3707 vdAddImageToList(pDisk, pImage);
3708 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
3709 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
3710 }
3711 else
3712 {
3713 /* Error detected, but image opened. Close and delete image. */
3714 rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, true);
3715 AssertRC(rc2);
3716 pImage->pvBackendData = NULL;
3717 }
3718 } while (0);
3719
3720 if (RT_UNLIKELY(fLockWrite))
3721 {
3722 rc2 = vdThreadFinishWrite(pDisk);
3723 AssertRC(rc2);
3724 }
3725 else if (RT_UNLIKELY(fLockRead))
3726 {
3727 rc2 = vdThreadFinishRead(pDisk);
3728 AssertRC(rc2);
3729 }
3730
3731 if (RT_FAILURE(rc))
3732 {
3733 if (pImage)
3734 {
3735 if (pImage->pszFilename)
3736 RTStrFree(pImage->pszFilename);
3737 RTMemFree(pImage);
3738 }
3739 }
3740
3741 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
3742 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
3743
3744 LogFlowFunc(("returns %Rrc\n", rc));
3745 return rc;
3746}
3747
3748/**
3749 * Creates and opens a new differencing image file in HDD container.
3750 * See comments for VDOpen function about differencing images.
3751 *
3752 * @returns VBox status code.
3753 * @param pDisk Pointer to HDD container.
3754 * @param pszBackend Name of the image file backend to use.
3755 * @param pszFilename Name of the differencing image file to create.
3756 * @param uImageFlags Flags specifying special image features.
3757 * @param pszComment Pointer to image comment. NULL is ok.
3758 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
3759 * @param pParentUuid New parent UUID of the image. If NULL, the UUID is queried automatically.
3760 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
3761 * @param pVDIfsImage Pointer to the per-image VD interface list.
3762 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
3763 */
3764VBOXDDU_DECL(int) VDCreateDiff(PVBOXHDD pDisk, const char *pszBackend,
3765 const char *pszFilename, unsigned uImageFlags,
3766 const char *pszComment, PCRTUUID pUuid,
3767 PCRTUUID pParentUuid, unsigned uOpenFlags,
3768 PVDINTERFACE pVDIfsImage,
3769 PVDINTERFACE pVDIfsOperation)
3770{
3771 int rc = VINF_SUCCESS;
3772 int rc2;
3773 bool fLockWrite = false, fLockRead = false;
3774 PVDIMAGE pImage = NULL;
3775 RTUUID uuid;
3776
3777 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid ParentUuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
3778 pDisk, pszBackend, pszFilename, uImageFlags, pszComment, pUuid, pParentUuid, uOpenFlags,
3779 pVDIfsImage, pVDIfsOperation));
3780
3781 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
3782 VDINTERFACETYPE_PROGRESS);
3783 PVDINTERFACEPROGRESS pCbProgress = NULL;
3784 if (pIfProgress)
3785 pCbProgress = VDGetInterfaceProgress(pIfProgress);
3786
3787 do
3788 {
3789 /* sanity check */
3790 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3791 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3792
3793 /* Check arguments. */
3794 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
3795 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
3796 rc = VERR_INVALID_PARAMETER);
3797 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
3798 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
3799 rc = VERR_INVALID_PARAMETER);
3800 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
3801 ("uImageFlags=%#x\n", uImageFlags),
3802 rc = VERR_INVALID_PARAMETER);
3803 /* The UUID may be NULL. */
3804 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
3805 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
3806 rc = VERR_INVALID_PARAMETER);
3807 /* The parent UUID may be NULL. */
3808 AssertMsgBreakStmt(pParentUuid == NULL || VALID_PTR(pParentUuid),
3809 ("pParentUuid=%#p ParentUUID=%RTuuid\n", pParentUuid, pParentUuid),
3810 rc = VERR_INVALID_PARAMETER);
3811 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
3812 ("uOpenFlags=%#x\n", uOpenFlags),
3813 rc = VERR_INVALID_PARAMETER);
3814
3815 /* Check state. Needs a temporary read lock. Holding the write lock
3816 * all the time would be blocking other activities for too long. */
3817 rc2 = vdThreadStartRead(pDisk);
3818 AssertRC(rc2);
3819 fLockRead = true;
3820 AssertMsgBreakStmt(pDisk->cImages != 0,
3821 ("Create diff image cannot be done without other images open\n"),
3822 rc = VERR_VD_INVALID_STATE);
3823 rc2 = vdThreadFinishRead(pDisk);
3824 AssertRC(rc2);
3825 fLockRead = false;
3826
3827 /* Set up image descriptor. */
3828 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
3829 if (!pImage)
3830 {
3831 rc = VERR_NO_MEMORY;
3832 break;
3833 }
3834 pImage->pszFilename = RTStrDup(pszFilename);
3835 if (!pImage->pszFilename)
3836 {
3837 rc = VERR_NO_MEMORY;
3838 break;
3839 }
3840
3841 rc = vdFindBackend(pszBackend, &pImage->Backend);
3842 if (RT_FAILURE(rc))
3843 break;
3844 if (!pImage->Backend)
3845 {
3846 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
3847 N_("VD: unknown backend name '%s'"), pszBackend);
3848 break;
3849 }
3850
3851 pImage->pDisk = pDisk;
3852 pImage->pVDIfsImage = pVDIfsImage;
3853
3854 /* Set up the I/O interface. */
3855 rc = VDInterfaceAdd(&pImage->VDIIO, "VD_IO", VDINTERFACETYPE_IO,
3856 &pDisk->VDIIOCallbacks, pImage, &pImage->pVDIfsImage);
3857 AssertRC(rc);
3858
3859 /* Create UUID if the caller didn't specify one. */
3860 if (!pUuid)
3861 {
3862 rc = RTUuidCreate(&uuid);
3863 if (RT_FAILURE(rc))
3864 {
3865 rc = vdError(pDisk, rc, RT_SRC_POS,
3866 N_("VD: cannot generate UUID for image '%s'"),
3867 pszFilename);
3868 break;
3869 }
3870 pUuid = &uuid;
3871 }
3872
3873 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
3874 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
3875 rc = pImage->Backend->pfnCreate(pImage->pszFilename, pDisk->cbSize,
3876 uImageFlags | VD_IMAGE_FLAGS_DIFF,
3877 pszComment, &pDisk->PCHSGeometry,
3878 &pDisk->LCHSGeometry, pUuid,
3879 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
3880 0, 99,
3881 pDisk->pVDIfsDisk,
3882 pImage->pVDIfsImage,
3883 pVDIfsOperation,
3884 &pImage->pvBackendData);
3885
3886 if (RT_SUCCESS(rc))
3887 {
3888 pImage->uImageFlags = uImageFlags;
3889
3890 /* Lock disk for writing, as we modify pDisk information below. */
3891 rc2 = vdThreadStartWrite(pDisk);
3892 AssertRC(rc2);
3893 fLockWrite = true;
3894
3895 /* Switch previous image to read-only mode. */
3896 unsigned uOpenFlagsPrevImg;
3897 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
3898 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
3899 {
3900 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
3901 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData, uOpenFlagsPrevImg);
3902 }
3903
3904 /** @todo optionally check UUIDs */
3905
3906 /* Re-check state, as the lock wasn't held and another image
3907 * creation call could have been done by another thread. */
3908 AssertMsgStmt(pDisk->cImages != 0,
3909 ("Create diff image cannot be done without other images open\n"),
3910 rc = VERR_VD_INVALID_STATE);
3911 }
3912
3913 if (RT_SUCCESS(rc))
3914 {
3915 RTUUID Uuid;
3916 RTTIMESPEC ts;
3917
3918 if (pParentUuid && !RTUuidIsNull(pParentUuid))
3919 {
3920 Uuid = *pParentUuid;
3921 pImage->Backend->pfnSetParentUuid(pImage->pvBackendData, &Uuid);
3922 }
3923 else
3924 {
3925 rc2 = pDisk->pLast->Backend->pfnGetUuid(pDisk->pLast->pvBackendData,
3926 &Uuid);
3927 if (RT_SUCCESS(rc2))
3928 pImage->Backend->pfnSetParentUuid(pImage->pvBackendData, &Uuid);
3929 }
3930 rc2 = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pvBackendData,
3931 &Uuid);
3932 if (RT_SUCCESS(rc2))
3933 pImage->Backend->pfnSetParentModificationUuid(pImage->pvBackendData,
3934 &Uuid);
3935 rc2 = pDisk->pLast->Backend->pfnGetTimeStamp(pDisk->pLast->pvBackendData,
3936 &ts);
3937 if (RT_SUCCESS(rc2))
3938 pImage->Backend->pfnSetParentTimeStamp(pImage->pvBackendData, &ts);
3939
3940 rc2 = pImage->Backend->pfnSetParentFilename(pImage->pvBackendData, pDisk->pLast->pszFilename);
3941 }
3942
3943 if (RT_SUCCESS(rc))
3944 {
3945 /* Image successfully opened, make it the last image. */
3946 vdAddImageToList(pDisk, pImage);
3947 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
3948 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
3949 }
3950 else
3951 {
3952 /* Error detected, but image opened. Close and delete image. */
3953 rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, true);
3954 AssertRC(rc2);
3955 pImage->pvBackendData = NULL;
3956 }
3957 } while (0);
3958
3959 if (RT_UNLIKELY(fLockWrite))
3960 {
3961 rc2 = vdThreadFinishWrite(pDisk);
3962 AssertRC(rc2);
3963 }
3964 else if (RT_UNLIKELY(fLockRead))
3965 {
3966 rc2 = vdThreadFinishRead(pDisk);
3967 AssertRC(rc2);
3968 }
3969
3970 if (RT_FAILURE(rc))
3971 {
3972 if (pImage)
3973 {
3974 if (pImage->pszFilename)
3975 RTStrFree(pImage->pszFilename);
3976 RTMemFree(pImage);
3977 }
3978 }
3979
3980 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
3981 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
3982
3983 LogFlowFunc(("returns %Rrc\n", rc));
3984 return rc;
3985}
3986
3987
3988/**
3989 * Merges two images (not necessarily with direct parent/child relationship).
3990 * As a side effect the source image and potentially the other images which
3991 * are also merged to the destination are deleted from both the disk and the
3992 * images in the HDD container.
3993 *
3994 * @returns VBox status code.
3995 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
3996 * @param pDisk Pointer to HDD container.
3997 * @param nImageFrom Name of the image file to merge from.
3998 * @param nImageTo Name of the image file to merge to.
3999 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
4000 */
4001VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
4002 unsigned nImageTo, PVDINTERFACE pVDIfsOperation)
4003{
4004 int rc = VINF_SUCCESS;
4005 int rc2;
4006 bool fLockWrite = false, fLockRead = false;
4007 void *pvBuf = NULL;
4008
4009 LogFlowFunc(("pDisk=%#p nImageFrom=%u nImageTo=%u pVDIfsOperation=%#p\n",
4010 pDisk, nImageFrom, nImageTo, pVDIfsOperation));
4011
4012 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
4013 VDINTERFACETYPE_PROGRESS);
4014 PVDINTERFACEPROGRESS pCbProgress = NULL;
4015 if (pIfProgress)
4016 pCbProgress = VDGetInterfaceProgress(pIfProgress);
4017
4018 do
4019 {
4020 /* sanity check */
4021 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4022 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4023
4024 /* For simplicity reasons lock for writing as the image reopen below
4025 * might need it. After all the reopen is usually needed. */
4026 rc2 = vdThreadStartWrite(pDisk);
4027 AssertRC(rc2);
4028 fLockRead = true;
4029 PVDIMAGE pImageFrom = vdGetImageByNumber(pDisk, nImageFrom);
4030 PVDIMAGE pImageTo = vdGetImageByNumber(pDisk, nImageTo);
4031 if (!pImageFrom || !pImageTo)
4032 {
4033 rc = VERR_VD_IMAGE_NOT_FOUND;
4034 break;
4035 }
4036 AssertBreakStmt(pImageFrom != pImageTo, rc = VERR_INVALID_PARAMETER);
4037
4038 /* Make sure destination image is writable. */
4039 unsigned uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pvBackendData);
4040 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
4041 {
4042 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
4043 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pvBackendData,
4044 uOpenFlags);
4045 if (RT_FAILURE(rc))
4046 break;
4047 }
4048
4049 /* Get size of destination image. */
4050 uint64_t cbSize = pImageTo->Backend->pfnGetSize(pImageTo->pvBackendData);
4051 rc2 = vdThreadFinishWrite(pDisk);
4052 AssertRC(rc2);
4053 fLockRead = false;
4054
4055 /* Allocate tmp buffer. */
4056 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
4057 if (!pvBuf)
4058 {
4059 rc = VERR_NO_MEMORY;
4060 break;
4061 }
4062
4063 /* Merging is done directly on the images itself. This potentially
4064 * causes trouble if the disk is full in the middle of operation. */
4065 if (nImageFrom < nImageTo)
4066 {
4067 /* Merge parent state into child. This means writing all not
4068 * allocated blocks in the destination image which are allocated in
4069 * the images to be merged. */
4070 uint64_t uOffset = 0;
4071 uint64_t cbRemaining = cbSize;
4072 do
4073 {
4074 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
4075
4076 /* Need to hold the write lock during a read-write operation. */
4077 rc2 = vdThreadStartWrite(pDisk);
4078 AssertRC(rc2);
4079 fLockWrite = true;
4080
4081 rc = pImageTo->Backend->pfnRead(pImageTo->pvBackendData,
4082 uOffset, pvBuf, cbThisRead,
4083 &cbThisRead);
4084 if (rc == VERR_VD_BLOCK_FREE)
4085 {
4086 /* Search for image with allocated block. Do not attempt to
4087 * read more than the previous reads marked as valid.
4088 * Otherwise this would return stale data when different
4089 * block sizes are used for the images. */
4090 for (PVDIMAGE pCurrImage = pImageTo->pPrev;
4091 pCurrImage != NULL && pCurrImage != pImageFrom->pPrev && rc == VERR_VD_BLOCK_FREE;
4092 pCurrImage = pCurrImage->pPrev)
4093 {
4094 rc = pCurrImage->Backend->pfnRead(pCurrImage->pvBackendData,
4095 uOffset, pvBuf,
4096 cbThisRead,
4097 &cbThisRead);
4098 }
4099
4100 if (rc != VERR_VD_BLOCK_FREE)
4101 {
4102 if (RT_FAILURE(rc))
4103 break;
4104 rc = vdWriteHelper(pDisk, pImageTo, pImageFrom->pPrev,
4105 uOffset, pvBuf,
4106 cbThisRead);
4107 if (RT_FAILURE(rc))
4108 break;
4109 }
4110 else
4111 rc = VINF_SUCCESS;
4112 }
4113 else if (RT_FAILURE(rc))
4114 break;
4115
4116 rc2 = vdThreadFinishWrite(pDisk);
4117 AssertRC(rc2);
4118 fLockWrite = false;
4119
4120 uOffset += cbThisRead;
4121 cbRemaining -= cbThisRead;
4122
4123 if (pCbProgress && pCbProgress->pfnProgress)
4124 {
4125 /** @todo r=klaus: this can update the progress to the same
4126 * percentage over and over again if the image format makes
4127 * relatively small increments. */
4128 rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
4129 uOffset * 99 / cbSize);
4130 if (RT_FAILURE(rc))
4131 break;
4132 }
4133 } while (uOffset < cbSize);
4134 }
4135 else
4136 {
4137 /*
4138 * We may need to update the parent uuid of the child coming after the
4139 * last image to be merged. We have to reopen it read/write.
4140 *
4141 * This is done before we do the actual merge to prevent an incosistent
4142 * chain if the mode change fails for some reason.
4143 */
4144 if (pImageFrom->pNext)
4145 {
4146 PVDIMAGE pImageChild = pImageFrom->pNext;
4147
4148 /* We need to open the image in read/write mode. */
4149 uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pvBackendData);
4150
4151 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
4152 {
4153 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
4154 rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pvBackendData,
4155 uOpenFlags);
4156 if (RT_FAILURE(rc))
4157 break;
4158 }
4159 }
4160
4161 /* Merge child state into parent. This means writing all blocks
4162 * which are allocated in the image up to the source image to the
4163 * destination image. */
4164 uint64_t uOffset = 0;
4165 uint64_t cbRemaining = cbSize;
4166 do
4167 {
4168 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
4169 rc = VERR_VD_BLOCK_FREE;
4170
4171 /* Need to hold the write lock during a read-write operation. */
4172 rc2 = vdThreadStartWrite(pDisk);
4173 AssertRC(rc2);
4174 fLockWrite = true;
4175
4176 /* Search for image with allocated block. Do not attempt to
4177 * read more than the previous reads marked as valid. Otherwise
4178 * this would return stale data when different block sizes are
4179 * used for the images. */
4180 for (PVDIMAGE pCurrImage = pImageFrom;
4181 pCurrImage != NULL && pCurrImage != pImageTo && rc == VERR_VD_BLOCK_FREE;
4182 pCurrImage = pCurrImage->pPrev)
4183 {
4184 rc = pCurrImage->Backend->pfnRead(pCurrImage->pvBackendData,
4185 uOffset, pvBuf,
4186 cbThisRead, &cbThisRead);
4187 }
4188
4189 if (rc != VERR_VD_BLOCK_FREE)
4190 {
4191 if (RT_FAILURE(rc))
4192 break;
4193 rc = vdWriteHelper(pDisk, pImageTo, NULL, uOffset, pvBuf,
4194 cbThisRead);
4195 if (RT_FAILURE(rc))
4196 break;
4197 }
4198 else
4199 rc = VINF_SUCCESS;
4200
4201 rc2 = vdThreadFinishWrite(pDisk);
4202 AssertRC(rc2);
4203 fLockWrite = true;
4204
4205 uOffset += cbThisRead;
4206 cbRemaining -= cbThisRead;
4207
4208 if (pCbProgress && pCbProgress->pfnProgress)
4209 {
4210 /** @todo r=klaus: this can update the progress to the same
4211 * percentage over and over again if the image format makes
4212 * relatively small increments. */
4213 rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
4214 uOffset * 99 / cbSize);
4215 if (RT_FAILURE(rc))
4216 break;
4217 }
4218 } while (uOffset < cbSize);
4219 }
4220
4221 /* Need to hold the write lock while finishing the merge. */
4222 rc2 = vdThreadStartWrite(pDisk);
4223 AssertRC(rc2);
4224 fLockWrite = true;
4225
4226 /* Update parent UUID so that image chain is consistent. */
4227 RTUUID Uuid;
4228 PVDIMAGE pImageChild = NULL;
4229 if (nImageFrom < nImageTo)
4230 {
4231 if (pImageFrom->pPrev)
4232 {
4233 rc = pImageFrom->pPrev->Backend->pfnGetUuid(pImageFrom->pPrev->pvBackendData,
4234 &Uuid);
4235 AssertRC(rc);
4236 }
4237 else
4238 RTUuidClear(&Uuid);
4239 rc = pImageTo->Backend->pfnSetParentUuid(pImageTo->pvBackendData,
4240 &Uuid);
4241 AssertRC(rc);
4242 }
4243 else
4244 {
4245 /* Update the parent uuid of the child of the last merged image. */
4246 if (pImageFrom->pNext)
4247 {
4248 rc = pImageTo->Backend->pfnGetUuid(pImageTo->pvBackendData,
4249 &Uuid);
4250 AssertRC(rc);
4251
4252 rc = pImageFrom->Backend->pfnSetParentUuid(pImageFrom->pNext->pvBackendData,
4253 &Uuid);
4254 AssertRC(rc);
4255
4256 pImageChild = pImageFrom->pNext;
4257 }
4258 }
4259
4260 /* Delete the no longer needed images. */
4261 PVDIMAGE pImg = pImageFrom, pTmp;
4262 while (pImg != pImageTo)
4263 {
4264 if (nImageFrom < nImageTo)
4265 pTmp = pImg->pNext;
4266 else
4267 pTmp = pImg->pPrev;
4268 vdRemoveImageFromList(pDisk, pImg);
4269 pImg->Backend->pfnClose(pImg->pvBackendData, true);
4270 RTMemFree(pImg->pszFilename);
4271 RTMemFree(pImg);
4272 pImg = pTmp;
4273 }
4274
4275 /* Make sure destination image is back to read only if necessary. */
4276 if (pImageTo != pDisk->pLast)
4277 {
4278 uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pvBackendData);
4279 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
4280 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pvBackendData,
4281 uOpenFlags);
4282 if (RT_FAILURE(rc))
4283 break;
4284 }
4285
4286 /*
4287 * Make sure the child is readonly
4288 * for the child -> parent merge direction
4289 * if neccessary.
4290 */
4291 if ( nImageFrom > nImageTo
4292 && pImageChild
4293 && pImageChild != pDisk->pLast)
4294 {
4295 uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pvBackendData);
4296 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
4297 rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pvBackendData,
4298 uOpenFlags);
4299 if (RT_FAILURE(rc))
4300 break;
4301 }
4302 } while (0);
4303
4304 if (RT_UNLIKELY(fLockWrite))
4305 {
4306 rc2 = vdThreadFinishWrite(pDisk);
4307 AssertRC(rc2);
4308 }
4309 else if (RT_UNLIKELY(fLockRead))
4310 {
4311 rc2 = vdThreadFinishRead(pDisk);
4312 AssertRC(rc2);
4313 }
4314
4315 if (pvBuf)
4316 RTMemTmpFree(pvBuf);
4317
4318 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
4319 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
4320
4321 LogFlowFunc(("returns %Rrc\n", rc));
4322 return rc;
4323}
4324
4325/**
4326 * Copies an image from one HDD container to another.
4327 * The copy is opened in the target HDD container.
4328 * It is possible to convert between different image formats, because the
4329 * backend for the destination may be different from the source.
4330 * If both the source and destination reference the same HDD container,
4331 * then the image is moved (by copying/deleting or renaming) to the new location.
4332 * The source container is unchanged if the move operation fails, otherwise
4333 * the image at the new location is opened in the same way as the old one was.
4334 *
4335 * @returns VBox status code.
4336 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
4337 * @param pDiskFrom Pointer to source HDD container.
4338 * @param nImage Image number, counts from 0. 0 is always base image of container.
4339 * @param pDiskTo Pointer to destination HDD container.
4340 * @param pszBackend Name of the image file backend to use.
4341 * @param pszFilename New name of the image (may be NULL if pDiskFrom == pDiskTo).
4342 * @param fMoveByRename If true, attempt to perform a move by renaming (if successful the new size is ignored).
4343 * @param cbSize New image size (0 means leave unchanged).
4344 * @param uImageFlags Flags specifying special destination image features.
4345 * @param pDstUuid New UUID of the destination image. If NULL, a new UUID is created.
4346 * This parameter is used if and only if a true copy is created.
4347 * In all rename/move cases the UUIDs are copied over.
4348 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
4349 * @param pDstVDIfsImage Pointer to the per-image VD interface list, for the
4350 * destination image.
4351 * @param pDstVDIfsOperation Pointer to the per-image VD interface list,
4352 * for the destination image.
4353 */
4354VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
4355 const char *pszBackend, const char *pszFilename,
4356 bool fMoveByRename, uint64_t cbSize,
4357 unsigned uImageFlags, PCRTUUID pDstUuid,
4358 PVDINTERFACE pVDIfsOperation,
4359 PVDINTERFACE pDstVDIfsImage,
4360 PVDINTERFACE pDstVDIfsOperation)
4361{
4362 int rc = VINF_SUCCESS;
4363 int rc2;
4364 bool fLockReadFrom = false, fLockWriteFrom = false, fLockWriteTo = false;
4365 void *pvBuf = NULL;
4366 PVDIMAGE pImageTo = NULL;
4367
4368 LogFlowFunc(("pDiskFrom=%#p nImage=%u pDiskTo=%#p pszBackend=\"%s\" pszFilename=\"%s\" fMoveByRename=%d cbSize=%llu pVDIfsOperation=%#p pDstVDIfsImage=%#p pDstVDIfsOperation=%#p\n",
4369 pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename, cbSize, pVDIfsOperation, pDstVDIfsImage, pDstVDIfsOperation));
4370
4371 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
4372 VDINTERFACETYPE_PROGRESS);
4373 PVDINTERFACEPROGRESS pCbProgress = NULL;
4374 if (pIfProgress)
4375 pCbProgress = VDGetInterfaceProgress(pIfProgress);
4376
4377 PVDINTERFACE pDstIfProgress = VDInterfaceGet(pDstVDIfsOperation,
4378 VDINTERFACETYPE_PROGRESS);
4379 PVDINTERFACEPROGRESS pDstCbProgress = NULL;
4380 if (pDstIfProgress)
4381 pDstCbProgress = VDGetInterfaceProgress(pDstIfProgress);
4382
4383 do {
4384 /* Check arguments. */
4385 AssertMsgBreakStmt(VALID_PTR(pDiskFrom), ("pDiskFrom=%#p\n", pDiskFrom),
4386 rc = VERR_INVALID_PARAMETER);
4387 AssertMsg(pDiskFrom->u32Signature == VBOXHDDDISK_SIGNATURE,
4388 ("u32Signature=%08x\n", pDiskFrom->u32Signature));
4389
4390 rc2 = vdThreadStartRead(pDiskFrom);
4391 AssertRC(rc2);
4392 fLockReadFrom = true;
4393 PVDIMAGE pImageFrom = vdGetImageByNumber(pDiskFrom, nImage);
4394 AssertPtrBreakStmt(pImageFrom, rc = VERR_VD_IMAGE_NOT_FOUND);
4395 AssertMsgBreakStmt(VALID_PTR(pDiskTo), ("pDiskTo=%#p\n", pDiskTo),
4396 rc = VERR_INVALID_PARAMETER);
4397 AssertMsg(pDiskTo->u32Signature == VBOXHDDDISK_SIGNATURE,
4398 ("u32Signature=%08x\n", pDiskTo->u32Signature));
4399
4400 /* Move the image. */
4401 if (pDiskFrom == pDiskTo)
4402 {
4403 /* Rename only works when backends are the same. */
4404 if ( fMoveByRename
4405 && !RTStrICmp(pszBackend, pImageFrom->Backend->pszBackendName))
4406 {
4407 rc2 = vdThreadFinishRead(pDiskFrom);
4408 AssertRC(rc2);
4409 fLockReadFrom = false;
4410
4411 rc2 = vdThreadStartWrite(pDiskFrom);
4412 AssertRC(rc2);
4413 fLockWriteFrom = true;
4414 rc = pImageFrom->Backend->pfnRename(pImageFrom->pvBackendData, pszFilename ? pszFilename : pImageFrom->pszFilename);
4415 break;
4416 }
4417
4418 /** @todo Moving (including shrinking/growing) of the image is
4419 * requested, but the rename attempt failed or it wasn't possible.
4420 * Must now copy image to temp location. */
4421 AssertReleaseMsgFailed(("VDCopy: moving by copy/delete not implemented\n"));
4422 }
4423
4424 /* pszFilename is allowed to be NULL, as this indicates copy to the existing image. */
4425 AssertMsgBreakStmt(pszFilename == NULL || (VALID_PTR(pszFilename) && *pszFilename),
4426 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
4427 rc = VERR_INVALID_PARAMETER);
4428
4429 uint64_t cbSizeFrom;
4430 cbSizeFrom = pImageFrom->Backend->pfnGetSize(pImageFrom->pvBackendData);
4431 if (cbSizeFrom == 0)
4432 {
4433 rc = VERR_VD_VALUE_NOT_FOUND;
4434 break;
4435 }
4436
4437 PDMMEDIAGEOMETRY PCHSGeometryFrom = {0, 0, 0};
4438 PDMMEDIAGEOMETRY LCHSGeometryFrom = {0, 0, 0};
4439 pImageFrom->Backend->pfnGetPCHSGeometry(pImageFrom->pvBackendData, &PCHSGeometryFrom);
4440 pImageFrom->Backend->pfnGetLCHSGeometry(pImageFrom->pvBackendData, &LCHSGeometryFrom);
4441
4442 RTUUID ImageUuid, ImageModificationUuid;
4443 if (pDiskFrom != pDiskTo)
4444 {
4445 if (pDstUuid)
4446 ImageUuid = *pDstUuid;
4447 else
4448 RTUuidCreate(&ImageUuid);
4449 }
4450 else
4451 {
4452 rc = pImageFrom->Backend->pfnGetUuid(pImageFrom->pvBackendData, &ImageUuid);
4453 if (RT_FAILURE(rc))
4454 RTUuidCreate(&ImageUuid);
4455 }
4456 rc = pImageFrom->Backend->pfnGetModificationUuid(pImageFrom->pvBackendData, &ImageModificationUuid);
4457 if (RT_FAILURE(rc))
4458 RTUuidClear(&ImageModificationUuid);
4459
4460 char szComment[1024];
4461 rc = pImageFrom->Backend->pfnGetComment(pImageFrom->pvBackendData, szComment, sizeof(szComment));
4462 if (RT_FAILURE(rc))
4463 szComment[0] = '\0';
4464 else
4465 szComment[sizeof(szComment) - 1] = '\0';
4466
4467 unsigned uOpenFlagsFrom;
4468 uOpenFlagsFrom = pImageFrom->Backend->pfnGetOpenFlags(pImageFrom->pvBackendData);
4469
4470 rc2 = vdThreadFinishRead(pDiskFrom);
4471 AssertRC(rc2);
4472 fLockReadFrom = false;
4473
4474 rc2 = vdThreadStartRead(pDiskTo);
4475 AssertRC(rc2);
4476 unsigned cImagesTo = pDiskTo->cImages;
4477 rc2 = vdThreadFinishRead(pDiskTo);
4478 AssertRC(rc2);
4479
4480 if (pszFilename)
4481 {
4482 if (cbSize == 0)
4483 cbSize = cbSizeFrom;
4484
4485 /* Create destination image with the properties of source image. */
4486 /** @todo replace the VDCreateDiff/VDCreateBase calls by direct
4487 * calls to the backend. Unifies the code and reduces the API
4488 * dependencies. Would also make the synchronization explicit. */
4489 if (cImagesTo > 0)
4490 {
4491 rc = VDCreateDiff(pDiskTo, pszBackend, pszFilename,
4492 uImageFlags, szComment, &ImageUuid,
4493 NULL /* pParentUuid */,
4494 uOpenFlagsFrom & ~VD_OPEN_FLAGS_READONLY,
4495 NULL, NULL);
4496
4497 rc2 = vdThreadStartWrite(pDiskTo);
4498 AssertRC(rc2);
4499 fLockWriteTo = true;
4500 } else {
4501 /** @todo hack to force creation of a fixed image for
4502 * the RAW backend, which can't handle anything else. */
4503 if (!RTStrICmp(pszBackend, "RAW"))
4504 uImageFlags |= VD_IMAGE_FLAGS_FIXED;
4505
4506 /* Fix broken PCHS geometry. Can happen for two reasons: either
4507 * the backend mixes up PCHS and LCHS, or the application used
4508 * to create the source image has put garbage in it. */
4509 /** @todo double-check if the VHD backend correctly handles
4510 * PCHS and LCHS geometry. also reconsider our current paranoia
4511 * level when it comes to geometry settings here and in the
4512 * backends. */
4513 if (PCHSGeometryFrom.cHeads > 16 || PCHSGeometryFrom.cSectors > 63)
4514 {
4515 Assert(RT_MIN(cbSize / 512 / 16 / 63, 16383) - (uint32_t)RT_MIN(cbSize / 512 / 16 / 63, 16383));
4516 PCHSGeometryFrom.cCylinders = (uint32_t)RT_MIN(cbSize / 512 / 16 / 63, 16383);
4517 PCHSGeometryFrom.cHeads = 16;
4518 PCHSGeometryFrom.cSectors = 63;
4519 }
4520
4521 rc = VDCreateBase(pDiskTo, pszBackend, pszFilename, cbSize,
4522 uImageFlags, szComment,
4523 &PCHSGeometryFrom, &LCHSGeometryFrom,
4524 NULL, uOpenFlagsFrom & ~VD_OPEN_FLAGS_READONLY, NULL, NULL);
4525
4526 rc2 = vdThreadStartWrite(pDiskTo);
4527 AssertRC(rc2);
4528 fLockWriteTo = true;
4529
4530 if (RT_SUCCESS(rc) && !RTUuidIsNull(&ImageUuid))
4531 pDiskTo->pLast->Backend->pfnSetUuid(pDiskTo->pLast->pvBackendData, &ImageUuid);
4532 }
4533 if (RT_FAILURE(rc))
4534 break;
4535
4536 pImageTo = pDiskTo->pLast;
4537 AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
4538
4539 cbSize = RT_MIN(cbSize, cbSizeFrom);
4540 }
4541 else
4542 {
4543 pImageTo = pDiskTo->pLast;
4544 AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
4545
4546 uint64_t cbSizeTo;
4547 cbSizeTo = pImageTo->Backend->pfnGetSize(pImageTo->pvBackendData);
4548 if (cbSizeTo == 0)
4549 {
4550 rc = VERR_VD_VALUE_NOT_FOUND;
4551 break;
4552 }
4553
4554 if (cbSize == 0)
4555 cbSize = RT_MIN(cbSizeFrom, cbSizeTo);
4556 }
4557
4558 rc2 = vdThreadFinishWrite(pDiskTo);
4559 AssertRC(rc2);
4560 fLockWriteTo = false;
4561
4562 /* Allocate tmp buffer. */
4563 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
4564 if (!pvBuf)
4565 {
4566 rc = VERR_NO_MEMORY;
4567 break;
4568 }
4569
4570 /* Whether we can take the optimized copy path (false) or not.
4571 * Don't optimize if the image existed or if it is a child image. */
4572 bool fRegularRead = (pszFilename == NULL) || (cImagesTo > 0);
4573
4574 /* Copy the data. */
4575 uint64_t uOffset = 0;
4576 uint64_t cbRemaining = cbSize;
4577
4578 do
4579 {
4580 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
4581
4582 /* Note that we don't attempt to synchronize cross-disk accesses.
4583 * It wouldn't be very difficult to do, just the lock order would
4584 * need to be defined somehow to prevent deadlocks. Postpone such
4585 * magic as there is no use case for this. */
4586
4587 rc2 = vdThreadStartRead(pDiskFrom);
4588 AssertRC(rc2);
4589 fLockReadFrom = true;
4590
4591 rc = vdReadHelper(pDiskFrom, pImageFrom, NULL, uOffset, pvBuf,
4592 cbThisRead, fRegularRead);
4593 if (RT_FAILURE(rc) && rc != VERR_VD_BLOCK_FREE)
4594 break;
4595
4596 rc2 = vdThreadFinishRead(pDiskFrom);
4597 AssertRC(rc2);
4598 fLockReadFrom = false;
4599
4600 if (rc != VERR_VD_BLOCK_FREE)
4601 {
4602 rc2 = vdThreadStartWrite(pDiskTo);
4603 AssertRC(rc2);
4604 fLockWriteTo = true;
4605
4606 rc = vdWriteHelper(pDiskTo, pImageTo, NULL, uOffset, pvBuf,
4607 cbThisRead);
4608 if (RT_FAILURE(rc))
4609 break;
4610
4611 rc2 = vdThreadFinishWrite(pDiskTo);
4612 AssertRC(rc2);
4613 fLockWriteTo = false;
4614 }
4615
4616 uOffset += cbThisRead;
4617 cbRemaining -= cbThisRead;
4618
4619 if (pCbProgress && pCbProgress->pfnProgress)
4620 {
4621 /** @todo r=klaus: this can update the progress to the same
4622 * percentage over and over again if the image format makes
4623 * relatively small increments. */
4624 rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
4625 uOffset * 99 / cbSize);
4626 if (RT_FAILURE(rc))
4627 break;
4628 }
4629 if (pDstCbProgress && pDstCbProgress->pfnProgress)
4630 {
4631 /** @todo r=klaus: this can update the progress to the same
4632 * percentage over and over again if the image format makes
4633 * relatively small increments. */
4634 rc = pDstCbProgress->pfnProgress(pDstIfProgress->pvUser,
4635 uOffset * 99 / cbSize);
4636 if (RT_FAILURE(rc))
4637 break;
4638 }
4639 } while (uOffset < cbSize);
4640
4641 if (RT_SUCCESS(rc))
4642 {
4643 rc2 = vdThreadStartWrite(pDiskTo);
4644 AssertRC(rc2);
4645 fLockWriteTo = true;
4646
4647 /* Only set modification UUID if it is non-null, since the source
4648 * backend might not provide a valid modification UUID. */
4649 if (!RTUuidIsNull(&ImageModificationUuid))
4650 pImageTo->Backend->pfnSetModificationUuid(pImageTo->pvBackendData, &ImageModificationUuid);
4651 }
4652 } while (0);
4653
4654 if (RT_FAILURE(rc) && pImageTo && pszFilename)
4655 {
4656 /* Take the write lock only if it is not taken. Not worth making the
4657 * above code even more complicated. */
4658 if (RT_UNLIKELY(!fLockWriteTo))
4659 {
4660 rc2 = vdThreadStartWrite(pDiskTo);
4661 AssertRC(rc2);
4662 fLockWriteTo = true;
4663 }
4664 /* Error detected, but new image created. Remove image from list. */
4665 vdRemoveImageFromList(pDiskTo, pImageTo);
4666
4667 /* Close and delete image. */
4668 rc2 = pImageTo->Backend->pfnClose(pImageTo->pvBackendData, true);
4669 AssertRC(rc2);
4670 pImageTo->pvBackendData = NULL;
4671
4672 /* Free remaining resources. */
4673 if (pImageTo->pszFilename)
4674 RTStrFree(pImageTo->pszFilename);
4675
4676 RTMemFree(pImageTo);
4677 }
4678
4679 if (RT_UNLIKELY(fLockWriteTo))
4680 {
4681 rc2 = vdThreadFinishWrite(pDiskTo);
4682 AssertRC(rc2);
4683 }
4684 if (RT_UNLIKELY(fLockWriteFrom))
4685 {
4686 rc2 = vdThreadFinishWrite(pDiskFrom);
4687 AssertRC(rc2);
4688 }
4689 else if (RT_UNLIKELY(fLockReadFrom))
4690 {
4691 rc2 = vdThreadFinishRead(pDiskFrom);
4692 AssertRC(rc2);
4693 }
4694
4695 if (pvBuf)
4696 RTMemTmpFree(pvBuf);
4697
4698 if (RT_SUCCESS(rc))
4699 {
4700 if (pCbProgress && pCbProgress->pfnProgress)
4701 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
4702 if (pDstCbProgress && pDstCbProgress->pfnProgress)
4703 pDstCbProgress->pfnProgress(pDstIfProgress->pvUser, 100);
4704 }
4705
4706 LogFlowFunc(("returns %Rrc\n", rc));
4707 return rc;
4708}
4709
4710/**
4711 * Optimizes the storage consumption of an image. Typically the unused blocks
4712 * have to be wiped with zeroes to achieve a substantial reduced storage use.
4713 * Another optimization done is reordering the image blocks, which can provide
4714 * a significant performance boost, as reads and writes tend to use less random
4715 * file offsets.
4716 *
4717 * @return VBox status code.
4718 * @return VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
4719 * @return VERR_VD_IMAGE_READ_ONLY if image is not writable.
4720 * @return VERR_NOT_SUPPORTED if this kind of image can be compacted, but
4721 * the code for this isn't implemented yet.
4722 * @param pDisk Pointer to HDD container.
4723 * @param nImage Image number, counts from 0. 0 is always base image of container.
4724 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
4725 */
4726VBOXDDU_DECL(int) VDCompact(PVBOXHDD pDisk, unsigned nImage,
4727 PVDINTERFACE pVDIfsOperation)
4728{
4729 int rc = VINF_SUCCESS;
4730 int rc2;
4731 bool fLockRead = false, fLockWrite = false;
4732 void *pvBuf = NULL;
4733 void *pvTmp = NULL;
4734
4735 LogFlowFunc(("pDisk=%#p nImage=%u pVDIfsOperation=%#p\n",
4736 pDisk, nImage, pVDIfsOperation));
4737
4738 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
4739 VDINTERFACETYPE_PROGRESS);
4740 PVDINTERFACEPROGRESS pCbProgress = NULL;
4741 if (pIfProgress)
4742 pCbProgress = VDGetInterfaceProgress(pIfProgress);
4743
4744 do {
4745 /* Check arguments. */
4746 AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
4747 rc = VERR_INVALID_PARAMETER);
4748 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE,
4749 ("u32Signature=%08x\n", pDisk->u32Signature));
4750
4751 rc2 = vdThreadStartRead(pDisk);
4752 AssertRC(rc2);
4753 fLockRead = true;
4754
4755 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
4756 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
4757
4758 /* If there is no compact callback for not file based backends then
4759 * the backend doesn't need compaction. No need to make much fuss about
4760 * this. For file based ones signal this as not yet supported. */
4761 if (!pImage->Backend->pfnCompact)
4762 {
4763 if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
4764 rc = VERR_NOT_SUPPORTED;
4765 else
4766 rc = VINF_SUCCESS;
4767 break;
4768 }
4769
4770 /* Insert interface for reading parent state into per-operation list,
4771 * if there is a parent image. */
4772 VDINTERFACE IfOpParent;
4773 VDINTERFACEPARENTSTATE ParentCb;
4774 VDPARENTSTATEDESC ParentUser;
4775 if (pImage->pPrev)
4776 {
4777 ParentCb.cbSize = sizeof(ParentCb);
4778 ParentCb.enmInterface = VDINTERFACETYPE_PARENTSTATE;
4779 ParentCb.pfnParentRead = vdParentRead;
4780 ParentUser.pDisk = pDisk;
4781 ParentUser.pImage = pImage->pPrev;
4782 rc = VDInterfaceAdd(&IfOpParent, "VDCompact_ParentState", VDINTERFACETYPE_PARENTSTATE,
4783 &ParentCb, &ParentUser, &pVDIfsOperation);
4784 AssertRC(rc);
4785 }
4786
4787 rc2 = vdThreadFinishRead(pDisk);
4788 AssertRC(rc2);
4789 fLockRead = false;
4790
4791 rc2 = vdThreadStartWrite(pDisk);
4792 AssertRC(rc2);
4793 fLockWrite = true;
4794
4795 rc = pImage->Backend->pfnCompact(pImage->pvBackendData,
4796 0, 99,
4797 pDisk->pVDIfsDisk,
4798 pImage->pVDIfsImage,
4799 pVDIfsOperation);
4800 } while (0);
4801
4802 if (RT_UNLIKELY(fLockWrite))
4803 {
4804 rc2 = vdThreadFinishWrite(pDisk);
4805 AssertRC(rc2);
4806 }
4807 else if (RT_UNLIKELY(fLockRead))
4808 {
4809 rc2 = vdThreadFinishRead(pDisk);
4810 AssertRC(rc2);
4811 }
4812
4813 if (pvBuf)
4814 RTMemTmpFree(pvBuf);
4815 if (pvTmp)
4816 RTMemTmpFree(pvTmp);
4817
4818 if (RT_SUCCESS(rc))
4819 {
4820 if (pCbProgress && pCbProgress->pfnProgress)
4821 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
4822 }
4823
4824 LogFlowFunc(("returns %Rrc\n", rc));
4825 return rc;
4826}
4827
4828/**
4829 * Closes the last opened image file in HDD container.
4830 * If previous image file was opened in read-only mode (the normal case) and
4831 * the last opened image is in read-write mode then the previous image will be
4832 * reopened in read/write mode.
4833 *
4834 * @returns VBox status code.
4835 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
4836 * @param pDisk Pointer to HDD container.
4837 * @param fDelete If true, delete the image from the host disk.
4838 */
4839VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete)
4840{
4841 int rc = VINF_SUCCESS;
4842 int rc2;
4843 bool fLockWrite = false;
4844
4845 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
4846 do
4847 {
4848 /* sanity check */
4849 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4850 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4851
4852 /* Not worth splitting this up into a read lock phase and write
4853 * lock phase, as closing an image is a relatively fast operation
4854 * dominated by the part which needs the write lock. */
4855 rc2 = vdThreadStartWrite(pDisk);
4856 AssertRC(rc2);
4857 fLockWrite = true;
4858
4859 PVDIMAGE pImage = pDisk->pLast;
4860 if (!pImage)
4861 {
4862 rc = VERR_VD_NOT_OPENED;
4863 break;
4864 }
4865 unsigned uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pvBackendData);
4866 /* Remove image from list of opened images. */
4867 vdRemoveImageFromList(pDisk, pImage);
4868 /* Close (and optionally delete) image. */
4869 rc = pImage->Backend->pfnClose(pImage->pvBackendData, fDelete);
4870 /* Free remaining resources related to the image. */
4871 RTStrFree(pImage->pszFilename);
4872 RTMemFree(pImage);
4873
4874 pImage = pDisk->pLast;
4875 if (!pImage)
4876 break;
4877
4878 /* If disk was previously in read/write mode, make sure it will stay
4879 * like this (if possible) after closing this image. Set the open flags
4880 * accordingly. */
4881 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
4882 {
4883 uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pvBackendData);
4884 uOpenFlags &= ~ VD_OPEN_FLAGS_READONLY;
4885 rc = pImage->Backend->pfnSetOpenFlags(pImage->pvBackendData, uOpenFlags);
4886 }
4887
4888 /* Cache disk information. */
4889 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
4890
4891 /* Cache PCHS geometry. */
4892 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
4893 &pDisk->PCHSGeometry);
4894 if (RT_FAILURE(rc2))
4895 {
4896 pDisk->PCHSGeometry.cCylinders = 0;
4897 pDisk->PCHSGeometry.cHeads = 0;
4898 pDisk->PCHSGeometry.cSectors = 0;
4899 }
4900 else
4901 {
4902 /* Make sure the PCHS geometry is properly clipped. */
4903 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
4904 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
4905 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
4906 }
4907
4908 /* Cache LCHS geometry. */
4909 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
4910 &pDisk->LCHSGeometry);
4911 if (RT_FAILURE(rc2))
4912 {
4913 pDisk->LCHSGeometry.cCylinders = 0;
4914 pDisk->LCHSGeometry.cHeads = 0;
4915 pDisk->LCHSGeometry.cSectors = 0;
4916 }
4917 else
4918 {
4919 /* Make sure the LCHS geometry is properly clipped. */
4920 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
4921 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
4922 }
4923 } while (0);
4924
4925 if (RT_UNLIKELY(fLockWrite))
4926 {
4927 rc2 = vdThreadFinishWrite(pDisk);
4928 AssertRC(rc2);
4929 }
4930
4931 LogFlowFunc(("returns %Rrc\n", rc));
4932 return rc;
4933}
4934
4935/**
4936 * Closes all opened image files in HDD container.
4937 *
4938 * @returns VBox status code.
4939 * @param pDisk Pointer to HDD container.
4940 */
4941VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk)
4942{
4943 int rc = VINF_SUCCESS;
4944 int rc2;
4945 bool fLockWrite = false;
4946
4947 LogFlowFunc(("pDisk=%#p\n", pDisk));
4948 do
4949 {
4950 /* sanity check */
4951 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4952 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4953
4954 /* Lock the entire operation. */
4955 rc2 = vdThreadStartWrite(pDisk);
4956 AssertRC(rc2);
4957 fLockWrite = true;
4958
4959 PVDIMAGE pImage = pDisk->pLast;
4960 while (VALID_PTR(pImage))
4961 {
4962 PVDIMAGE pPrev = pImage->pPrev;
4963 /* Remove image from list of opened images. */
4964 vdRemoveImageFromList(pDisk, pImage);
4965 /* Close image. */
4966 rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, false);
4967 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
4968 rc = rc2;
4969 /* Free remaining resources related to the image. */
4970 RTStrFree(pImage->pszFilename);
4971 RTMemFree(pImage);
4972 pImage = pPrev;
4973 }
4974 Assert(!VALID_PTR(pDisk->pLast));
4975 } while (0);
4976
4977 if (RT_UNLIKELY(fLockWrite))
4978 {
4979 rc2 = vdThreadFinishWrite(pDisk);
4980 AssertRC(rc2);
4981 }
4982
4983 LogFlowFunc(("returns %Rrc\n", rc));
4984 return rc;
4985}
4986
4987/**
4988 * Read data from virtual HDD.
4989 *
4990 * @returns VBox status code.
4991 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
4992 * @param pDisk Pointer to HDD container.
4993 * @param uOffset Offset of first reading byte from start of disk.
4994 * @param pvBuf Pointer to buffer for reading data.
4995 * @param cbRead Number of bytes to read.
4996 */
4997VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuf,
4998 size_t cbRead)
4999{
5000 int rc = VINF_SUCCESS;
5001 int rc2;
5002 bool fLockRead = false;
5003
5004 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbRead=%zu\n",
5005 pDisk, uOffset, pvBuf, cbRead));
5006 do
5007 {
5008 /* sanity check */
5009 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
5010 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5011
5012 /* Check arguments. */
5013 AssertMsgBreakStmt(VALID_PTR(pvBuf),
5014 ("pvBuf=%#p\n", pvBuf),
5015 rc = VERR_INVALID_PARAMETER);
5016 AssertMsgBreakStmt(cbRead,
5017 ("cbRead=%zu\n", cbRead),
5018 rc = VERR_INVALID_PARAMETER);
5019
5020 rc2 = vdThreadStartRead(pDisk);
5021 AssertRC(rc2);
5022 fLockRead = true;
5023
5024 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
5025 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
5026 uOffset, cbRead, pDisk->cbSize),
5027 rc = VERR_INVALID_PARAMETER);
5028
5029 PVDIMAGE pImage = pDisk->pLast;
5030 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
5031
5032 rc = vdReadHelper(pDisk, pImage, NULL, uOffset, pvBuf, cbRead, true);
5033 } while (0);
5034
5035 if (RT_UNLIKELY(fLockRead))
5036 {
5037 rc2 = vdThreadFinishRead(pDisk);
5038 AssertRC(rc2);
5039 }
5040
5041 LogFlowFunc(("returns %Rrc\n", rc));
5042 return rc;
5043}
5044
5045/**
5046 * Write data to virtual HDD.
5047 *
5048 * @returns VBox status code.
5049 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
5050 * @param pDisk Pointer to HDD container.
5051 * @param uOffset Offset of the first byte being
5052 * written from start of disk.
5053 * @param pvBuf Pointer to buffer for writing data.
5054 * @param cbWrite Number of bytes to write.
5055 */
5056VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf,
5057 size_t cbWrite)
5058{
5059 int rc = VINF_SUCCESS;
5060 int rc2;
5061 bool fLockWrite = false;
5062
5063 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbWrite=%zu\n",
5064 pDisk, uOffset, pvBuf, cbWrite));
5065 do
5066 {
5067 /* sanity check */
5068 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
5069 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5070
5071 /* Check arguments. */
5072 AssertMsgBreakStmt(VALID_PTR(pvBuf),
5073 ("pvBuf=%#p\n", pvBuf),
5074 rc = VERR_INVALID_PARAMETER);
5075 AssertMsgBreakStmt(cbWrite,
5076 ("cbWrite=%zu\n", cbWrite),
5077 rc = VERR_INVALID_PARAMETER);
5078
5079 rc2 = vdThreadStartWrite(pDisk);
5080 AssertRC(rc2);
5081 fLockWrite = true;
5082
5083 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
5084 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
5085 uOffset, cbWrite, pDisk->cbSize),
5086 rc = VERR_INVALID_PARAMETER);
5087
5088 PVDIMAGE pImage = pDisk->pLast;
5089 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
5090
5091 vdSetModifiedFlag(pDisk);
5092 rc = vdWriteHelper(pDisk, pImage, NULL, uOffset, pvBuf, cbWrite);
5093 } while (0);
5094
5095 if (RT_UNLIKELY(fLockWrite))
5096 {
5097 rc2 = vdThreadFinishWrite(pDisk);
5098 AssertRC(rc2);
5099 }
5100
5101 LogFlowFunc(("returns %Rrc\n", rc));
5102 return rc;
5103}
5104
5105/**
5106 * Make sure the on disk representation of a virtual HDD is up to date.
5107 *
5108 * @returns VBox status code.
5109 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
5110 * @param pDisk Pointer to HDD container.
5111 */
5112VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
5113{
5114 int rc = VINF_SUCCESS;
5115 int rc2;
5116 bool fLockWrite = false;
5117
5118 LogFlowFunc(("pDisk=%#p\n", pDisk));
5119 do
5120 {
5121 /* sanity check */
5122 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
5123 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5124
5125 rc2 = vdThreadStartWrite(pDisk);
5126 AssertRC(rc2);
5127 fLockWrite = true;
5128
5129 PVDIMAGE pImage = pDisk->pLast;
5130 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
5131
5132 vdResetModifiedFlag(pDisk);
5133 rc = pImage->Backend->pfnFlush(pImage->pvBackendData);
5134 } while (0);
5135
5136 if (RT_UNLIKELY(fLockWrite))
5137 {
5138 rc2 = vdThreadFinishWrite(pDisk);
5139 AssertRC(rc2);
5140 }
5141
5142 LogFlowFunc(("returns %Rrc\n", rc));
5143 return rc;
5144}
5145
5146/**
5147 * Get number of opened images in HDD container.
5148 *
5149 * @returns Number of opened images for HDD container. 0 if no images have been opened.
5150 * @param pDisk Pointer to HDD container.
5151 */
5152VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk)
5153{
5154 unsigned cImages;
5155 int rc2;
5156 bool fLockRead = false;
5157
5158 LogFlowFunc(("pDisk=%#p\n", pDisk));
5159 do
5160 {
5161 /* sanity check */
5162 AssertPtrBreakStmt(pDisk, cImages = 0);
5163 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5164
5165 rc2 = vdThreadStartRead(pDisk);
5166 AssertRC(rc2);
5167 fLockRead = true;
5168
5169 cImages = pDisk->cImages;
5170 } while (0);
5171
5172 if (RT_UNLIKELY(fLockRead))
5173 {
5174 rc2 = vdThreadFinishRead(pDisk);
5175 AssertRC(rc2);
5176 }
5177
5178 LogFlowFunc(("returns %u\n", cImages));
5179 return cImages;
5180}
5181
5182/**
5183 * Get read/write mode of HDD container.
5184 *
5185 * @returns Virtual disk ReadOnly status.
5186 * @returns true if no image is opened in HDD container.
5187 * @param pDisk Pointer to HDD container.
5188 */
5189VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
5190{
5191 bool fReadOnly;
5192 int rc2;
5193 bool fLockRead = false;
5194
5195 LogFlowFunc(("pDisk=%#p\n", pDisk));
5196 do
5197 {
5198 /* sanity check */
5199 AssertPtrBreakStmt(pDisk, fReadOnly = false);
5200 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5201
5202 rc2 = vdThreadStartRead(pDisk);
5203 AssertRC(rc2);
5204 fLockRead = true;
5205
5206 PVDIMAGE pImage = pDisk->pLast;
5207 AssertPtrBreakStmt(pImage, fReadOnly = true);
5208
5209 unsigned uOpenFlags;
5210 uOpenFlags = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
5211 fReadOnly = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
5212 } while (0);
5213
5214 if (RT_UNLIKELY(fLockRead))
5215 {
5216 rc2 = vdThreadFinishRead(pDisk);
5217 AssertRC(rc2);
5218 }
5219
5220 LogFlowFunc(("returns %d\n", fReadOnly));
5221 return fReadOnly;
5222}
5223
5224/**
5225 * Get total capacity of an image in HDD container.
5226 *
5227 * @returns Virtual disk size in bytes.
5228 * @returns 0 if no image with specified number was not opened.
5229 * @param pDisk Pointer to HDD container.
5230 * @param nImage Image number, counds from 0. 0 is always base image of container.
5231 */
5232VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk, unsigned nImage)
5233{
5234 uint64_t cbSize;
5235 int rc2;
5236 bool fLockRead = false;
5237
5238 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
5239 do
5240 {
5241 /* sanity check */
5242 AssertPtrBreakStmt(pDisk, cbSize = 0);
5243 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5244
5245 rc2 = vdThreadStartRead(pDisk);
5246 AssertRC(rc2);
5247 fLockRead = true;
5248
5249 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
5250 AssertPtrBreakStmt(pImage, cbSize = 0);
5251 cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
5252 } while (0);
5253
5254 if (RT_UNLIKELY(fLockRead))
5255 {
5256 rc2 = vdThreadFinishRead(pDisk);
5257 AssertRC(rc2);
5258 }
5259
5260 LogFlowFunc(("returns %llu\n", cbSize));
5261 return cbSize;
5262}
5263
5264/**
5265 * Get total file size of an image in HDD container.
5266 *
5267 * @returns Virtual disk size in bytes.
5268 * @returns 0 if no image is opened in HDD container.
5269 * @param pDisk Pointer to HDD container.
5270 * @param nImage Image number, counts from 0. 0 is always base image of container.
5271 */
5272VBOXDDU_DECL(uint64_t) VDGetFileSize(PVBOXHDD pDisk, unsigned nImage)
5273{
5274 uint64_t cbSize;
5275 int rc2;
5276 bool fLockRead = false;
5277
5278 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
5279 do
5280 {
5281 /* sanity check */
5282 AssertPtrBreakStmt(pDisk, cbSize = 0);
5283 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5284
5285 rc2 = vdThreadStartRead(pDisk);
5286 AssertRC(rc2);
5287 fLockRead = true;
5288
5289 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
5290 AssertPtrBreakStmt(pImage, cbSize = 0);
5291 cbSize = pImage->Backend->pfnGetFileSize(pImage->pvBackendData);
5292 } while (0);
5293
5294 if (RT_UNLIKELY(fLockRead))
5295 {
5296 rc2 = vdThreadFinishRead(pDisk);
5297 AssertRC(rc2);
5298 }
5299
5300 LogFlowFunc(("returns %llu\n", cbSize));
5301 return cbSize;
5302}
5303
5304/**
5305 * Get virtual disk PCHS geometry stored in HDD container.
5306 *
5307 * @returns VBox status code.
5308 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
5309 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
5310 * @param pDisk Pointer to HDD container.
5311 * @param nImage Image number, counts from 0. 0 is always base image of container.
5312 * @param pPCHSGeometry Where to store PCHS geometry. Not NULL.
5313 */
5314VBOXDDU_DECL(int) VDGetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
5315 PPDMMEDIAGEOMETRY pPCHSGeometry)
5316{
5317 int rc = VINF_SUCCESS;
5318 int rc2;
5319 bool fLockRead = false;
5320
5321 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p\n",
5322 pDisk, nImage, pPCHSGeometry));
5323 do
5324 {
5325 /* sanity check */
5326 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
5327 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5328
5329 /* Check arguments. */
5330 AssertMsgBreakStmt(VALID_PTR(pPCHSGeometry),
5331 ("pPCHSGeometry=%#p\n", pPCHSGeometry),
5332 rc = VERR_INVALID_PARAMETER);
5333
5334 rc2 = vdThreadStartRead(pDisk);
5335 AssertRC(rc2);
5336 fLockRead = true;
5337
5338 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
5339 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
5340
5341 if (pImage == pDisk->pLast)
5342 {
5343 /* Use cached information if possible. */
5344 if (pDisk->PCHSGeometry.cCylinders != 0)
5345 *pPCHSGeometry = pDisk->PCHSGeometry;
5346 else
5347 rc = VERR_VD_GEOMETRY_NOT_SET;
5348 }
5349 else
5350 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
5351 pPCHSGeometry);
5352 } while (0);
5353
5354 if (RT_UNLIKELY(fLockRead))
5355 {
5356 rc2 = vdThreadFinishRead(pDisk);
5357 AssertRC(rc2);
5358 }
5359
5360 LogFlowFunc(("%s: %Rrc (PCHS=%u/%u/%u)\n", __FUNCTION__, rc,
5361 pDisk->PCHSGeometry.cCylinders, pDisk->PCHSGeometry.cHeads,
5362 pDisk->PCHSGeometry.cSectors));
5363 return rc;
5364}
5365
5366/**
5367 * Store virtual disk PCHS geometry in HDD container.
5368 *
5369 * Note that in case of unrecoverable error all images in HDD container will be closed.
5370 *
5371 * @returns VBox status code.
5372 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
5373 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
5374 * @param pDisk Pointer to HDD container.
5375 * @param nImage Image number, counts from 0. 0 is always base image of container.
5376 * @param pPCHSGeometry Where to load PCHS geometry from. Not NULL.
5377 */
5378VBOXDDU_DECL(int) VDSetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
5379 PCPDMMEDIAGEOMETRY pPCHSGeometry)
5380{
5381 int rc = VINF_SUCCESS;
5382 int rc2;
5383 bool fLockWrite = false;
5384
5385 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
5386 pDisk, nImage, pPCHSGeometry, pPCHSGeometry->cCylinders,
5387 pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
5388 do
5389 {
5390 /* sanity check */
5391 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
5392 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5393
5394 /* Check arguments. */
5395 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
5396 && pPCHSGeometry->cHeads <= 16
5397 && pPCHSGeometry->cSectors <= 63,
5398 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
5399 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
5400 pPCHSGeometry->cSectors),
5401 rc = VERR_INVALID_PARAMETER);
5402
5403 rc2 = vdThreadStartWrite(pDisk);
5404 AssertRC(rc2);
5405 fLockWrite = true;
5406
5407 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
5408 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
5409
5410 if (pImage == pDisk->pLast)
5411 {
5412 if ( pPCHSGeometry->cCylinders != pDisk->PCHSGeometry.cCylinders
5413 || pPCHSGeometry->cHeads != pDisk->PCHSGeometry.cHeads
5414 || pPCHSGeometry->cSectors != pDisk->PCHSGeometry.cSectors)
5415 {
5416 /* Only update geometry if it is changed. Avoids similar checks
5417 * in every backend. Most of the time the new geometry is set
5418 * to the previous values, so no need to go through the hassle
5419 * of updating an image which could be opened in read-only mode
5420 * right now. */
5421 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pvBackendData,
5422 pPCHSGeometry);
5423
5424 /* Cache new geometry values in any case. */
5425 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
5426 &pDisk->PCHSGeometry);
5427 if (RT_FAILURE(rc2))
5428 {
5429 pDisk->PCHSGeometry.cCylinders = 0;
5430 pDisk->PCHSGeometry.cHeads = 0;
5431 pDisk->PCHSGeometry.cSectors = 0;
5432 }
5433 else
5434 {
5435 /* Make sure the CHS geometry is properly clipped. */
5436 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 255);
5437 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
5438 }
5439 }
5440 }
5441 else
5442 {
5443 PDMMEDIAGEOMETRY PCHS;
5444 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
5445 &PCHS);
5446 if ( RT_FAILURE(rc)
5447 || pPCHSGeometry->cCylinders != PCHS.cCylinders
5448 || pPCHSGeometry->cHeads != PCHS.cHeads
5449 || pPCHSGeometry->cSectors != PCHS.cSectors)
5450 {
5451 /* Only update geometry if it is changed. Avoids similar checks
5452 * in every backend. Most of the time the new geometry is set
5453 * to the previous values, so no need to go through the hassle
5454 * of updating an image which could be opened in read-only mode
5455 * right now. */
5456 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pvBackendData,
5457 pPCHSGeometry);
5458 }
5459 }
5460 } while (0);
5461
5462 if (RT_UNLIKELY(fLockWrite))
5463 {
5464 rc2 = vdThreadFinishWrite(pDisk);
5465 AssertRC(rc2);
5466 }
5467
5468 LogFlowFunc(("returns %Rrc\n", rc));
5469 return rc;
5470}
5471
5472/**
5473 * Get virtual disk LCHS geometry stored in HDD container.
5474 *
5475 * @returns VBox status code.
5476 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
5477 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
5478 * @param pDisk Pointer to HDD container.
5479 * @param nImage Image number, counts from 0. 0 is always base image of container.
5480 * @param pLCHSGeometry Where to store LCHS geometry. Not NULL.
5481 */
5482VBOXDDU_DECL(int) VDGetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
5483 PPDMMEDIAGEOMETRY pLCHSGeometry)
5484{
5485 int rc = VINF_SUCCESS;
5486 int rc2;
5487 bool fLockRead = false;
5488
5489 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p\n",
5490 pDisk, nImage, pLCHSGeometry));
5491 do
5492 {
5493 /* sanity check */
5494 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
5495 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5496
5497 /* Check arguments. */
5498 AssertMsgBreakStmt(VALID_PTR(pLCHSGeometry),
5499 ("pLCHSGeometry=%#p\n", pLCHSGeometry),
5500 rc = VERR_INVALID_PARAMETER);
5501
5502 rc2 = vdThreadStartRead(pDisk);
5503 AssertRC(rc2);
5504 fLockRead = true;
5505
5506 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
5507 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
5508
5509 if (pImage == pDisk->pLast)
5510 {
5511 /* Use cached information if possible. */
5512 if (pDisk->LCHSGeometry.cCylinders != 0)
5513 *pLCHSGeometry = pDisk->LCHSGeometry;
5514 else
5515 rc = VERR_VD_GEOMETRY_NOT_SET;
5516 }
5517 else
5518 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
5519 pLCHSGeometry);
5520 } while (0);
5521
5522 if (RT_UNLIKELY(fLockRead))
5523 {
5524 rc2 = vdThreadFinishRead(pDisk);
5525 AssertRC(rc2);
5526 }
5527
5528 LogFlowFunc((": %Rrc (LCHS=%u/%u/%u)\n", rc,
5529 pDisk->LCHSGeometry.cCylinders, pDisk->LCHSGeometry.cHeads,
5530 pDisk->LCHSGeometry.cSectors));
5531 return rc;
5532}
5533
5534/**
5535 * Store virtual disk LCHS geometry in HDD container.
5536 *
5537 * Note that in case of unrecoverable error all images in HDD container will be closed.
5538 *
5539 * @returns VBox status code.
5540 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
5541 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
5542 * @param pDisk Pointer to HDD container.
5543 * @param nImage Image number, counts from 0. 0 is always base image of container.
5544 * @param pLCHSGeometry Where to load LCHS geometry from. Not NULL.
5545 */
5546VBOXDDU_DECL(int) VDSetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
5547 PCPDMMEDIAGEOMETRY pLCHSGeometry)
5548{
5549 int rc = VINF_SUCCESS;
5550 int rc2;
5551 bool fLockWrite = false;
5552
5553 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
5554 pDisk, nImage, pLCHSGeometry, pLCHSGeometry->cCylinders,
5555 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
5556 do
5557 {
5558 /* sanity check */
5559 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
5560 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5561
5562 /* Check arguments. */
5563 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
5564 && pLCHSGeometry->cHeads <= 255
5565 && pLCHSGeometry->cSectors <= 63,
5566 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
5567 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
5568 pLCHSGeometry->cSectors),
5569 rc = VERR_INVALID_PARAMETER);
5570
5571 rc2 = vdThreadStartWrite(pDisk);
5572 AssertRC(rc2);
5573 fLockWrite = true;
5574
5575 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
5576 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
5577
5578 if (pImage == pDisk->pLast)
5579 {
5580 if ( pLCHSGeometry->cCylinders != pDisk->LCHSGeometry.cCylinders
5581 || pLCHSGeometry->cHeads != pDisk->LCHSGeometry.cHeads
5582 || pLCHSGeometry->cSectors != pDisk->LCHSGeometry.cSectors)
5583 {
5584 /* Only update geometry if it is changed. Avoids similar checks
5585 * in every backend. Most of the time the new geometry is set
5586 * to the previous values, so no need to go through the hassle
5587 * of updating an image which could be opened in read-only mode
5588 * right now. */
5589 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pvBackendData,
5590 pLCHSGeometry);
5591
5592 /* Cache new geometry values in any case. */
5593 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
5594 &pDisk->LCHSGeometry);
5595 if (RT_FAILURE(rc2))
5596 {
5597 pDisk->LCHSGeometry.cCylinders = 0;
5598 pDisk->LCHSGeometry.cHeads = 0;
5599 pDisk->LCHSGeometry.cSectors = 0;
5600 }
5601 else
5602 {
5603 /* Make sure the CHS geometry is properly clipped. */
5604 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
5605 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
5606 }
5607 }
5608 }
5609 else
5610 {
5611 PDMMEDIAGEOMETRY LCHS;
5612 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
5613 &LCHS);
5614 if ( RT_FAILURE(rc)
5615 || pLCHSGeometry->cCylinders != LCHS.cCylinders
5616 || pLCHSGeometry->cHeads != LCHS.cHeads
5617 || pLCHSGeometry->cSectors != LCHS.cSectors)
5618 {
5619 /* Only update geometry if it is changed. Avoids similar checks
5620 * in every backend. Most of the time the new geometry is set
5621 * to the previous values, so no need to go through the hassle
5622 * of updating an image which could be opened in read-only mode
5623 * right now. */
5624 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pvBackendData,
5625 pLCHSGeometry);
5626 }
5627 }
5628 } while (0);
5629
5630 if (RT_UNLIKELY(fLockWrite))
5631 {
5632 rc2 = vdThreadFinishWrite(pDisk);
5633 AssertRC(rc2);
5634 }
5635
5636 LogFlowFunc(("returns %Rrc\n", rc));
5637 return rc;
5638}
5639
5640/**
5641 * Get version of image in HDD container.
5642 *
5643 * @returns VBox status code.
5644 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
5645 * @param pDisk Pointer to HDD container.
5646 * @param nImage Image number, counts from 0. 0 is always base image of container.
5647 * @param puVersion Where to store the image version.
5648 */
5649VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
5650 unsigned *puVersion)
5651{
5652 int rc = VINF_SUCCESS;
5653 int rc2;
5654 bool fLockRead = false;
5655
5656 LogFlowFunc(("pDisk=%#p nImage=%u puVersion=%#p\n",
5657 pDisk, nImage, puVersion));
5658 do
5659 {
5660 /* sanity check */
5661 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
5662 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5663
5664 /* Check arguments. */
5665 AssertMsgBreakStmt(VALID_PTR(puVersion),
5666 ("puVersion=%#p\n", puVersion),
5667 rc = VERR_INVALID_PARAMETER);
5668
5669 rc2 = vdThreadStartRead(pDisk);
5670 AssertRC(rc2);
5671 fLockRead = true;
5672
5673 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
5674 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
5675
5676 *puVersion = pImage->Backend->pfnGetVersion(pImage->pvBackendData);
5677 } while (0);
5678
5679 if (RT_UNLIKELY(fLockRead))
5680 {
5681 rc2 = vdThreadFinishRead(pDisk);
5682 AssertRC(rc2);
5683 }
5684
5685 LogFlowFunc(("returns %Rrc uVersion=%#x\n", rc, *puVersion));
5686 return rc;
5687}
5688
5689/**
5690 * List the capabilities of image backend in HDD container.
5691 *
5692 * @returns VBox status code.
5693 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
5694 * @param pDisk Pointer to the HDD container.
5695 * @param nImage Image number, counts from 0. 0 is always base image of container.
5696 * @param pbackendInfo Where to store the backend information.
5697 */
5698VBOXDDU_DECL(int) VDBackendInfoSingle(PVBOXHDD pDisk, unsigned nImage,
5699 PVDBACKENDINFO pBackendInfo)
5700{
5701 int rc = VINF_SUCCESS;
5702 int rc2;
5703 bool fLockRead = false;
5704
5705 LogFlowFunc(("pDisk=%#p nImage=%u pBackendInfo=%#p\n",
5706 pDisk, nImage, pBackendInfo));
5707 do
5708 {
5709 /* sanity check */
5710 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
5711 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5712
5713 /* Check arguments. */
5714 AssertMsgBreakStmt(VALID_PTR(pBackendInfo),
5715 ("pBackendInfo=%#p\n", pBackendInfo),
5716 rc = VERR_INVALID_PARAMETER);
5717
5718 rc2 = vdThreadStartRead(pDisk);
5719 AssertRC(rc2);
5720 fLockRead = true;
5721
5722 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
5723 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
5724
5725 pBackendInfo->pszBackend = pImage->Backend->pszBackendName;
5726 pBackendInfo->uBackendCaps = pImage->Backend->uBackendCaps;
5727 pBackendInfo->papszFileExtensions = pImage->Backend->papszFileExtensions;
5728 pBackendInfo->paConfigInfo = pImage->Backend->paConfigInfo;
5729 } while (0);
5730
5731 if (RT_UNLIKELY(fLockRead))
5732 {
5733 rc2 = vdThreadFinishRead(pDisk);
5734 AssertRC(rc2);
5735 }
5736
5737 LogFlowFunc(("returns %Rrc\n", rc));
5738 return rc;
5739}
5740
5741/**
5742 * Get flags of image in HDD container.
5743 *
5744 * @returns VBox status code.
5745 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
5746 * @param pDisk Pointer to HDD container.
5747 * @param nImage Image number, counts from 0. 0 is always base image of container.
5748 * @param puImageFlags Where to store the image flags.
5749 */
5750VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage,
5751 unsigned *puImageFlags)
5752{
5753 int rc = VINF_SUCCESS;
5754 int rc2;
5755 bool fLockRead = false;
5756
5757 LogFlowFunc(("pDisk=%#p nImage=%u puImageFlags=%#p\n",
5758 pDisk, nImage, puImageFlags));
5759 do
5760 {
5761 /* sanity check */
5762 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
5763 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5764
5765 /* Check arguments. */
5766 AssertMsgBreakStmt(VALID_PTR(puImageFlags),
5767 ("puImageFlags=%#p\n", puImageFlags),
5768 rc = VERR_INVALID_PARAMETER);
5769
5770 rc2 = vdThreadStartRead(pDisk);
5771 AssertRC(rc2);
5772 fLockRead = true;
5773
5774 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
5775 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
5776
5777 *puImageFlags = pImage->uImageFlags;
5778 } while (0);
5779
5780 if (RT_UNLIKELY(fLockRead))
5781 {
5782 rc2 = vdThreadFinishRead(pDisk);
5783 AssertRC(rc2);
5784 }
5785
5786 LogFlowFunc(("returns %Rrc uImageFlags=%#x\n", rc, *puImageFlags));
5787 return rc;
5788}
5789
5790/**
5791 * Get open flags of image in HDD container.
5792 *
5793 * @returns VBox status code.
5794 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
5795 * @param pDisk Pointer to HDD container.
5796 * @param nImage Image number, counts from 0. 0 is always base image of container.
5797 * @param puOpenFlags Where to store the image open flags.
5798 */
5799VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
5800 unsigned *puOpenFlags)
5801{
5802 int rc = VINF_SUCCESS;
5803 int rc2;
5804 bool fLockRead = false;
5805
5806 LogFlowFunc(("pDisk=%#p nImage=%u puOpenFlags=%#p\n",
5807 pDisk, nImage, puOpenFlags));
5808 do
5809 {
5810 /* sanity check */
5811 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
5812 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5813
5814 /* Check arguments. */
5815 AssertMsgBreakStmt(VALID_PTR(puOpenFlags),
5816 ("puOpenFlags=%#p\n", puOpenFlags),
5817 rc = VERR_INVALID_PARAMETER);
5818
5819 rc2 = vdThreadStartRead(pDisk);
5820 AssertRC(rc2);
5821 fLockRead = true;
5822
5823 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
5824 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
5825
5826 *puOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pvBackendData);
5827 } while (0);
5828
5829 if (RT_UNLIKELY(fLockRead))
5830 {
5831 rc2 = vdThreadFinishRead(pDisk);
5832 AssertRC(rc2);
5833 }
5834
5835 LogFlowFunc(("returns %Rrc uOpenFlags=%#x\n", rc, *puOpenFlags));
5836 return rc;
5837}
5838
5839/**
5840 * Set open flags of image in HDD container.
5841 * This operation may cause file locking changes and/or files being reopened.
5842 * Note that in case of unrecoverable error all images in HDD container will be closed.
5843 *
5844 * @returns VBox status code.
5845 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
5846 * @param pDisk Pointer to HDD container.
5847 * @param nImage Image number, counts from 0. 0 is always base image of container.
5848 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
5849 */
5850VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
5851 unsigned uOpenFlags)
5852{
5853 int rc;
5854 int rc2;
5855 bool fLockWrite = false;
5856
5857 LogFlowFunc(("pDisk=%#p uOpenFlags=%#u\n", pDisk, uOpenFlags));
5858 do
5859 {
5860 /* sanity check */
5861 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
5862 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5863
5864 /* Check arguments. */
5865 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
5866 ("uOpenFlags=%#x\n", uOpenFlags),
5867 rc = VERR_INVALID_PARAMETER);
5868
5869 rc2 = vdThreadStartWrite(pDisk);
5870 AssertRC(rc2);
5871 fLockWrite = true;
5872
5873 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
5874 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
5875
5876 rc = pImage->Backend->pfnSetOpenFlags(pImage->pvBackendData,
5877 uOpenFlags);
5878 } while (0);
5879
5880 if (RT_UNLIKELY(fLockWrite))
5881 {
5882 rc2 = vdThreadFinishWrite(pDisk);
5883 AssertRC(rc2);
5884 }
5885
5886 LogFlowFunc(("returns %Rrc\n", rc));
5887 return rc;
5888}
5889
5890/**
5891 * Get base filename of image in HDD container. Some image formats use
5892 * other filenames as well, so don't use this for anything but informational
5893 * purposes.
5894 *
5895 * @returns VBox status code.
5896 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
5897 * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
5898 * @param pDisk Pointer to HDD container.
5899 * @param nImage Image number, counts from 0. 0 is always base image of container.
5900 * @param pszFilename Where to store the image file name.
5901 * @param cbFilename Size of buffer pszFilename points to.
5902 */
5903VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
5904 char *pszFilename, unsigned cbFilename)
5905{
5906 int rc;
5907 int rc2;
5908 bool fLockRead = false;
5909
5910 LogFlowFunc(("pDisk=%#p nImage=%u pszFilename=%#p cbFilename=%u\n",
5911 pDisk, nImage, pszFilename, cbFilename));
5912 do
5913 {
5914 /* sanity check */
5915 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
5916 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5917
5918 /* Check arguments. */
5919 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
5920 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
5921 rc = VERR_INVALID_PARAMETER);
5922 AssertMsgBreakStmt(cbFilename,
5923 ("cbFilename=%u\n", cbFilename),
5924 rc = VERR_INVALID_PARAMETER);
5925
5926 rc2 = vdThreadStartRead(pDisk);
5927 AssertRC(rc2);
5928 fLockRead = true;
5929
5930 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
5931 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
5932
5933 size_t cb = strlen(pImage->pszFilename);
5934 if (cb <= cbFilename)
5935 {
5936 strcpy(pszFilename, pImage->pszFilename);
5937 rc = VINF_SUCCESS;
5938 }
5939 else
5940 {
5941 strncpy(pszFilename, pImage->pszFilename, cbFilename - 1);
5942 pszFilename[cbFilename - 1] = '\0';
5943 rc = VERR_BUFFER_OVERFLOW;
5944 }
5945 } while (0);
5946
5947 if (RT_UNLIKELY(fLockRead))
5948 {
5949 rc2 = vdThreadFinishRead(pDisk);
5950 AssertRC(rc2);
5951 }
5952
5953 LogFlowFunc(("returns %Rrc, pszFilename=\"%s\"\n", rc, pszFilename));
5954 return rc;
5955}
5956
5957/**
5958 * Get the comment line of image in HDD container.
5959 *
5960 * @returns VBox status code.
5961 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
5962 * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
5963 * @param pDisk Pointer to HDD container.
5964 * @param nImage Image number, counts from 0. 0 is always base image of container.
5965 * @param pszComment Where to store the comment string of image. NULL is ok.
5966 * @param cbComment The size of pszComment buffer. 0 is ok.
5967 */
5968VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
5969 char *pszComment, unsigned cbComment)
5970{
5971 int rc;
5972 int rc2;
5973 bool fLockRead = false;
5974
5975 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p cbComment=%u\n",
5976 pDisk, nImage, pszComment, cbComment));
5977 do
5978 {
5979 /* sanity check */
5980 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
5981 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5982
5983 /* Check arguments. */
5984 AssertMsgBreakStmt(VALID_PTR(pszComment),
5985 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
5986 rc = VERR_INVALID_PARAMETER);
5987 AssertMsgBreakStmt(cbComment,
5988 ("cbComment=%u\n", cbComment),
5989 rc = VERR_INVALID_PARAMETER);
5990
5991 rc2 = vdThreadStartRead(pDisk);
5992 AssertRC(rc2);
5993 fLockRead = true;
5994
5995 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
5996 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
5997
5998 rc = pImage->Backend->pfnGetComment(pImage->pvBackendData, pszComment,
5999 cbComment);
6000 } while (0);
6001
6002 if (RT_UNLIKELY(fLockRead))
6003 {
6004 rc2 = vdThreadFinishRead(pDisk);
6005 AssertRC(rc2);
6006 }
6007
6008 LogFlowFunc(("returns %Rrc, pszComment=\"%s\"\n", rc, pszComment));
6009 return rc;
6010}
6011
6012/**
6013 * Changes the comment line of image in HDD container.
6014 *
6015 * @returns VBox status code.
6016 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
6017 * @param pDisk Pointer to HDD container.
6018 * @param nImage Image number, counts from 0. 0 is always base image of container.
6019 * @param pszComment New comment string (UTF-8). NULL is allowed to reset the comment.
6020 */
6021VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
6022 const char *pszComment)
6023{
6024 int rc;
6025 int rc2;
6026 bool fLockWrite = false;
6027
6028 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p \"%s\"\n",
6029 pDisk, nImage, pszComment, pszComment));
6030 do
6031 {
6032 /* sanity check */
6033 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6034 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6035
6036 /* Check arguments. */
6037 AssertMsgBreakStmt(VALID_PTR(pszComment) || pszComment == NULL,
6038 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
6039 rc = VERR_INVALID_PARAMETER);
6040
6041 rc2 = vdThreadStartWrite(pDisk);
6042 AssertRC(rc2);
6043 fLockWrite = true;
6044
6045 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6046 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
6047
6048 rc = pImage->Backend->pfnSetComment(pImage->pvBackendData, pszComment);
6049 } while (0);
6050
6051 if (RT_UNLIKELY(fLockWrite))
6052 {
6053 rc2 = vdThreadFinishWrite(pDisk);
6054 AssertRC(rc2);
6055 }
6056
6057 LogFlowFunc(("returns %Rrc\n", rc));
6058 return rc;
6059}
6060
6061
6062/**
6063 * Get UUID of image in HDD container.
6064 *
6065 * @returns VBox status code.
6066 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
6067 * @param pDisk Pointer to HDD container.
6068 * @param nImage Image number, counts from 0. 0 is always base image of container.
6069 * @param pUuid Where to store the image creation UUID.
6070 */
6071VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
6072{
6073 int rc;
6074 int rc2;
6075 bool fLockRead = false;
6076
6077 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
6078 do
6079 {
6080 /* sanity check */
6081 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6082 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6083
6084 /* Check arguments. */
6085 AssertMsgBreakStmt(VALID_PTR(pUuid),
6086 ("pUuid=%#p\n", pUuid),
6087 rc = VERR_INVALID_PARAMETER);
6088
6089 rc2 = vdThreadStartRead(pDisk);
6090 AssertRC(rc2);
6091 fLockRead = true;
6092
6093 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6094 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
6095
6096 rc = pImage->Backend->pfnGetUuid(pImage->pvBackendData, pUuid);
6097 } while (0);
6098
6099 if (RT_UNLIKELY(fLockRead))
6100 {
6101 rc2 = vdThreadFinishRead(pDisk);
6102 AssertRC(rc2);
6103 }
6104
6105 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
6106 return rc;
6107}
6108
6109/**
6110 * Set the image's UUID. Should not be used by normal applications.
6111 *
6112 * @returns VBox status code.
6113 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
6114 * @param pDisk Pointer to HDD container.
6115 * @param nImage Image number, counts from 0. 0 is always base image of container.
6116 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
6117 */
6118VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
6119{
6120 int rc;
6121 int rc2;
6122 bool fLockWrite = false;
6123
6124 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
6125 pDisk, nImage, pUuid, pUuid));
6126 do
6127 {
6128 /* sanity check */
6129 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6130 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6131
6132 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
6133 ("pUuid=%#p\n", pUuid),
6134 rc = VERR_INVALID_PARAMETER);
6135
6136 rc2 = vdThreadStartWrite(pDisk);
6137 AssertRC(rc2);
6138 fLockWrite = true;
6139
6140 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6141 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
6142
6143 RTUUID Uuid;
6144 if (!pUuid)
6145 {
6146 RTUuidCreate(&Uuid);
6147 pUuid = &Uuid;
6148 }
6149 rc = pImage->Backend->pfnSetUuid(pImage->pvBackendData, pUuid);
6150 } while (0);
6151
6152 if (RT_UNLIKELY(fLockWrite))
6153 {
6154 rc2 = vdThreadFinishWrite(pDisk);
6155 AssertRC(rc2);
6156 }
6157
6158 LogFlowFunc(("returns %Rrc\n", rc));
6159 return rc;
6160}
6161
6162/**
6163 * Get last modification UUID of image in HDD container.
6164 *
6165 * @returns VBox status code.
6166 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
6167 * @param pDisk Pointer to HDD container.
6168 * @param nImage Image number, counts from 0. 0 is always base image of container.
6169 * @param pUuid Where to store the image modification UUID.
6170 */
6171VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
6172{
6173 int rc = VINF_SUCCESS;
6174 int rc2;
6175 bool fLockRead = false;
6176
6177 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
6178 do
6179 {
6180 /* sanity check */
6181 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6182 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6183
6184 /* Check arguments. */
6185 AssertMsgBreakStmt(VALID_PTR(pUuid),
6186 ("pUuid=%#p\n", pUuid),
6187 rc = VERR_INVALID_PARAMETER);
6188
6189 rc2 = vdThreadStartRead(pDisk);
6190 AssertRC(rc2);
6191 fLockRead = true;
6192
6193 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6194 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
6195
6196 rc = pImage->Backend->pfnGetModificationUuid(pImage->pvBackendData,
6197 pUuid);
6198 } while (0);
6199
6200 if (RT_UNLIKELY(fLockRead))
6201 {
6202 rc2 = vdThreadFinishRead(pDisk);
6203 AssertRC(rc2);
6204 }
6205
6206 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
6207 return rc;
6208}
6209
6210/**
6211 * Set the image's last modification UUID. Should not be used by normal applications.
6212 *
6213 * @returns VBox status code.
6214 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
6215 * @param pDisk Pointer to HDD container.
6216 * @param nImage Image number, counts from 0. 0 is always base image of container.
6217 * @param pUuid New modification UUID of the image. If NULL, a new UUID is created.
6218 */
6219VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
6220{
6221 int rc;
6222 int rc2;
6223 bool fLockWrite = false;
6224
6225 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
6226 pDisk, nImage, pUuid, pUuid));
6227 do
6228 {
6229 /* sanity check */
6230 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6231 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6232
6233 /* Check arguments. */
6234 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
6235 ("pUuid=%#p\n", pUuid),
6236 rc = VERR_INVALID_PARAMETER);
6237
6238 rc2 = vdThreadStartWrite(pDisk);
6239 AssertRC(rc2);
6240 fLockWrite = true;
6241
6242 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6243 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
6244
6245 RTUUID Uuid;
6246 if (!pUuid)
6247 {
6248 RTUuidCreate(&Uuid);
6249 pUuid = &Uuid;
6250 }
6251 rc = pImage->Backend->pfnSetModificationUuid(pImage->pvBackendData,
6252 pUuid);
6253 } while (0);
6254
6255 if (RT_UNLIKELY(fLockWrite))
6256 {
6257 rc2 = vdThreadFinishWrite(pDisk);
6258 AssertRC(rc2);
6259 }
6260
6261 LogFlowFunc(("returns %Rrc\n", rc));
6262 return rc;
6263}
6264
6265/**
6266 * Get parent UUID of image in HDD container.
6267 *
6268 * @returns VBox status code.
6269 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
6270 * @param pDisk Pointer to HDD container.
6271 * @param nImage Image number, counts from 0. 0 is always base image of container.
6272 * @param pUuid Where to store the parent image UUID.
6273 */
6274VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
6275 PRTUUID pUuid)
6276{
6277 int rc = VINF_SUCCESS;
6278 int rc2;
6279 bool fLockRead = false;
6280
6281 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
6282 do
6283 {
6284 /* sanity check */
6285 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6286 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6287
6288 /* Check arguments. */
6289 AssertMsgBreakStmt(VALID_PTR(pUuid),
6290 ("pUuid=%#p\n", pUuid),
6291 rc = VERR_INVALID_PARAMETER);
6292
6293 rc2 = vdThreadStartRead(pDisk);
6294 AssertRC(rc2);
6295 fLockRead = true;
6296
6297 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6298 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
6299
6300 rc = pImage->Backend->pfnGetParentUuid(pImage->pvBackendData, pUuid);
6301 } while (0);
6302
6303 if (RT_UNLIKELY(fLockRead))
6304 {
6305 rc2 = vdThreadFinishRead(pDisk);
6306 AssertRC(rc2);
6307 }
6308
6309 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
6310 return rc;
6311}
6312
6313/**
6314 * Set the image's parent UUID. Should not be used by normal applications.
6315 *
6316 * @returns VBox status code.
6317 * @param pDisk Pointer to HDD container.
6318 * @param nImage Image number, counts from 0. 0 is always base image of container.
6319 * @param pUuid New parent UUID of the image. If NULL, a new UUID is created.
6320 */
6321VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
6322 PCRTUUID pUuid)
6323{
6324 int rc;
6325 int rc2;
6326 bool fLockWrite = false;
6327
6328 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
6329 pDisk, nImage, pUuid, pUuid));
6330 do
6331 {
6332 /* sanity check */
6333 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6334 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6335
6336 /* Check arguments. */
6337 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
6338 ("pUuid=%#p\n", pUuid),
6339 rc = VERR_INVALID_PARAMETER);
6340
6341 rc2 = vdThreadStartWrite(pDisk);
6342 AssertRC(rc2);
6343 fLockWrite = true;
6344
6345 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6346 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
6347
6348 RTUUID Uuid;
6349 if (!pUuid)
6350 {
6351 RTUuidCreate(&Uuid);
6352 pUuid = &Uuid;
6353 }
6354 rc = pImage->Backend->pfnSetParentUuid(pImage->pvBackendData, pUuid);
6355 } while (0);
6356
6357 if (RT_UNLIKELY(fLockWrite))
6358 {
6359 rc2 = vdThreadFinishWrite(pDisk);
6360 AssertRC(rc2);
6361 }
6362
6363 LogFlowFunc(("returns %Rrc\n", rc));
6364 return rc;
6365}
6366
6367
6368/**
6369 * Debug helper - dumps all opened images in HDD container into the log file.
6370 *
6371 * @param pDisk Pointer to HDD container.
6372 */
6373VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk)
6374{
6375 int rc2;
6376 bool fLockRead = false;
6377
6378 do
6379 {
6380 /* sanity check */
6381 AssertPtrBreak(pDisk);
6382 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6383
6384 int (*pfnMessage)(void *, const char *, ...) = NULL;
6385 void *pvUser = pDisk->pInterfaceError->pvUser;
6386
6387 if (pDisk->pInterfaceErrorCallbacks && VALID_PTR(pDisk->pInterfaceErrorCallbacks->pfnMessage))
6388 pfnMessage = pDisk->pInterfaceErrorCallbacks->pfnMessage;
6389 else
6390 {
6391 pDisk->pInterfaceErrorCallbacks->pfnMessage = vdLogMessage;
6392 pfnMessage = vdLogMessage;
6393 }
6394
6395 rc2 = vdThreadStartRead(pDisk);
6396 AssertRC(rc2);
6397 fLockRead = true;
6398
6399 pfnMessage(pvUser, "--- Dumping VD Disk, Images=%u\n", pDisk->cImages);
6400 for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
6401 {
6402 pfnMessage(pvUser, "Dumping VD image \"%s\" (Backend=%s)\n",
6403 pImage->pszFilename, pImage->Backend->pszBackendName);
6404 pImage->Backend->pfnDump(pImage->pvBackendData);
6405 }
6406 } while (0);
6407
6408 if (RT_UNLIKELY(fLockRead))
6409 {
6410 rc2 = vdThreadFinishRead(pDisk);
6411 AssertRC(rc2);
6412 }
6413}
6414
6415/**
6416 * Query if asynchronous operations are supported for this disk.
6417 *
6418 * @returns VBox status code.
6419 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
6420 * @param pDisk Pointer to the HDD container.
6421 * @param nImage Image number, counts from 0. 0 is always base image of container.
6422 * @param pfAIOSupported Where to store if async IO is supported.
6423 */
6424VBOXDDU_DECL(int) VDImageIsAsyncIOSupported(PVBOXHDD pDisk, unsigned nImage, bool *pfAIOSupported)
6425{
6426 int rc = VINF_SUCCESS;
6427 int rc2;
6428 bool fLockRead = false;
6429
6430 LogFlowFunc(("pDisk=%#p nImage=%u pfAIOSupported=%#p\n", pDisk, nImage, pfAIOSupported));
6431 do
6432 {
6433 /* sanity check */
6434 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6435 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6436
6437 /* Check arguments. */
6438 AssertMsgBreakStmt(VALID_PTR(pfAIOSupported),
6439 ("pfAIOSupported=%#p\n", pfAIOSupported),
6440 rc = VERR_INVALID_PARAMETER);
6441
6442 rc2 = vdThreadStartRead(pDisk);
6443 AssertRC(rc2);
6444 fLockRead = true;
6445
6446 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6447 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
6448
6449 if (pImage->Backend->uBackendCaps & VD_CAP_ASYNC)
6450 *pfAIOSupported = pImage->Backend->pfnIsAsyncIOSupported(pImage->pvBackendData);
6451 else
6452 *pfAIOSupported = false;
6453 } while (0);
6454
6455 if (RT_UNLIKELY(fLockRead))
6456 {
6457 rc2 = vdThreadFinishRead(pDisk);
6458 AssertRC(rc2);
6459 }
6460
6461 LogFlowFunc(("returns %Rrc, fAIOSupported=%u\n", rc, *pfAIOSupported));
6462 return rc;
6463}
6464
6465
6466VBOXDDU_DECL(int) VDAsyncRead(PVBOXHDD pDisk, uint64_t uOffset, size_t cbRead,
6467 PCRTSGSEG paSeg, unsigned cSeg,
6468 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
6469 void *pvUser1, void *pvUser2)
6470{
6471 int rc = VERR_VD_BLOCK_FREE;
6472 int rc2;
6473 bool fLockRead = false;
6474 PVDIOCTX pIoCtx = NULL;
6475
6476 LogFlowFunc(("pDisk=%#p uOffset=%llu paSeg=%p cSeg=%u cbRead=%zu pvUser1=%#p pvUser2=%#p\n",
6477 pDisk, uOffset, paSeg, cSeg, cbRead, pvUser1, pvUser2));
6478
6479 do
6480 {
6481 /* sanity check */
6482 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6483 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6484
6485 /* Check arguments. */
6486 AssertMsgBreakStmt(cbRead,
6487 ("cbRead=%zu\n", cbRead),
6488 rc = VERR_INVALID_PARAMETER);
6489 AssertMsgBreakStmt(VALID_PTR(paSeg),
6490 ("paSeg=%#p\n", paSeg),
6491 rc = VERR_INVALID_PARAMETER);
6492 AssertMsgBreakStmt(cSeg,
6493 ("cSeg=%zu\n", cSeg),
6494 rc = VERR_INVALID_PARAMETER);
6495
6496 rc2 = vdThreadStartRead(pDisk);
6497 AssertRC(rc2);
6498 fLockRead = true;
6499
6500 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
6501 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
6502 uOffset, cbRead, pDisk->cbSize),
6503 rc = VERR_INVALID_PARAMETER);
6504
6505 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_READ, uOffset,
6506 cbRead, paSeg, cSeg,
6507 pfnComplete, pvUser1, pvUser2,
6508 NULL, vdReadHelperAsync);
6509 if (!pIoCtx)
6510 {
6511 rc = VERR_NO_MEMORY;
6512 break;
6513 }
6514
6515 pIoCtx->pImage = pDisk->pLast;
6516 AssertPtrBreakStmt(pIoCtx->pImage, rc = VERR_VD_NOT_OPENED);
6517
6518 rc = vdIoCtxProcess(pIoCtx);
6519 if (rc == VINF_VD_ASYNC_IO_FINISHED)
6520 {
6521 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
6522 vdIoCtxFree(pDisk, pIoCtx);
6523 else
6524 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
6525 }
6526 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
6527 vdIoCtxFree(pDisk, pIoCtx);
6528
6529 } while (0);
6530
6531 if (RT_UNLIKELY(fLockRead) && ( rc == VINF_VD_ASYNC_IO_FINISHED
6532 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
6533 {
6534 rc2 = vdThreadFinishRead(pDisk);
6535 AssertRC(rc2);
6536 }
6537
6538 LogFlowFunc(("returns %Rrc\n", rc));
6539 return rc;
6540}
6541
6542
6543VBOXDDU_DECL(int) VDAsyncWrite(PVBOXHDD pDisk, uint64_t uOffset, size_t cbWrite,
6544 PCRTSGSEG paSeg, unsigned cSeg,
6545 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
6546 void *pvUser1, void *pvUser2)
6547{
6548 int rc;
6549 int rc2;
6550 bool fLockWrite = false;
6551 PVDIOCTX pIoCtx = NULL;
6552
6553 LogFlowFunc(("pDisk=%#p uOffset=%llu paSeg=%p cSeg=%u cbWrite=%zu pvUser1=%#p pvUser2=%#p\n",
6554 pDisk, uOffset, paSeg, cSeg, cbWrite, pvUser1, pvUser2));
6555 do
6556 {
6557 /* sanity check */
6558 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6559 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6560
6561 /* Check arguments. */
6562 AssertMsgBreakStmt(cbWrite,
6563 ("cbWrite=%zu\n", cbWrite),
6564 rc = VERR_INVALID_PARAMETER);
6565 AssertMsgBreakStmt(VALID_PTR(paSeg),
6566 ("paSeg=%#p\n", paSeg),
6567 rc = VERR_INVALID_PARAMETER);
6568 AssertMsgBreakStmt(cSeg,
6569 ("cSeg=%zu\n", cSeg),
6570 rc = VERR_INVALID_PARAMETER);
6571
6572 rc2 = vdThreadStartWrite(pDisk);
6573 AssertRC(rc2);
6574 fLockWrite = true;
6575
6576 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
6577 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
6578 uOffset, cbWrite, pDisk->cbSize),
6579 rc = VERR_INVALID_PARAMETER);
6580
6581 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_WRITE, uOffset,
6582 cbWrite, paSeg, cSeg,
6583 pfnComplete, pvUser1, pvUser2,
6584 NULL, vdWriteHelperAsync);
6585 if (!pIoCtx)
6586 {
6587 rc = VERR_NO_MEMORY;
6588 break;
6589 }
6590
6591 PVDIMAGE pImage = pDisk->pLast;
6592 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
6593 pIoCtx->pImage = pImage;
6594
6595 rc = vdIoCtxProcess(pIoCtx);
6596 if (rc == VINF_VD_ASYNC_IO_FINISHED)
6597 {
6598 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
6599 vdIoCtxFree(pDisk, pIoCtx);
6600 else
6601 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
6602 }
6603 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
6604 vdIoCtxFree(pDisk, pIoCtx);
6605 } while (0);
6606
6607 if (RT_UNLIKELY(fLockWrite) && ( rc == VINF_VD_ASYNC_IO_FINISHED
6608 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
6609 {
6610 rc2 = vdThreadFinishWrite(pDisk);
6611 AssertRC(rc2);
6612 }
6613
6614 LogFlowFunc(("returns %Rrc\n", rc));
6615 return rc;
6616}
6617
6618
6619VBOXDDU_DECL(int) VDAsyncFlush(PVBOXHDD pDisk, PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
6620 void *pvUser1, void *pvUser2)
6621{
6622 int rc;
6623 int rc2;
6624 bool fLockWrite = false;
6625 PVDIOCTX pIoCtx = NULL;
6626
6627 LogFlowFunc(("pDisk=%#p\n", pDisk));
6628
6629 do
6630 {
6631 /* sanity check */
6632 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6633 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6634
6635 rc2 = vdThreadStartWrite(pDisk);
6636 AssertRC(rc2);
6637 fLockWrite = true;
6638
6639 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_FLUSH, 0,
6640 0, NULL, 0,
6641 pfnComplete, pvUser1, pvUser2,
6642 NULL, vdFlushHelperAsync);
6643 if (!pIoCtx)
6644 {
6645 rc = VERR_NO_MEMORY;
6646 break;
6647 }
6648
6649 PVDIMAGE pImage = pDisk->pLast;
6650 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
6651 pIoCtx->pImage = pImage;
6652
6653 rc = vdIoCtxProcess(pIoCtx);
6654 if (rc == VINF_VD_ASYNC_IO_FINISHED)
6655 {
6656 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
6657 vdIoCtxFree(pDisk, pIoCtx);
6658 else
6659 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
6660 }
6661 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
6662 vdIoCtxFree(pDisk, pIoCtx);
6663 } while (0);
6664
6665 if (RT_UNLIKELY(fLockWrite) && ( rc == VINF_VD_ASYNC_IO_FINISHED
6666 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
6667 {
6668 rc2 = vdThreadFinishWrite(pDisk);
6669 AssertRC(rc2);
6670 }
6671
6672 LogFlowFunc(("returns %Rrc\n", rc));
6673 return rc;
6674}
6675
6676#if 0
6677/** @copydoc VBOXHDDBACKEND::pfnComposeLocation */
6678int genericFileComposeLocation(PVDINTERFACE pConfig, char **pszLocation)
6679{
6680 return NULL;
6681}
6682
6683
6684/** @copydoc VBOXHDDBACKEND::pfnComposeName */
6685int genericFileComposeName(PVDINTERFACE pConfig, char **pszName)
6686{
6687 return NULL;
6688}
6689#endif
Note: See TracBrowser for help on using the repository browser.

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