VirtualBox

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

最後變更 在這個檔案從28684是 28683,由 vboxsync 提交於 15 年 前

VBoxHDD: Fix hanging async I/O

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

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