VirtualBox

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

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

warning

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