VirtualBox

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

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

VBoxHDD.cpp: Init rcReq field

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

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