VirtualBox

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

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

VBoxHDD: Add a flag to disable locking of an image file

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

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