VirtualBox

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

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

VD/Async: Full support for VHD images. VMDK is still in progress

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

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