VirtualBox

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

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

VBoxHDD: Fix an assertion during a flush request. There is no S/G list

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

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