VirtualBox

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

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

Fix double free

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

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