VirtualBox

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

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

VBoxHDD: Fix

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

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