VirtualBox

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

Last change on this file since 31514 was 31504, checked in by vboxsync, 14 years ago

static tstVDSetUuid experiment

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

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