VirtualBox

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

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

VBoxHDD: Move the FinishWrite call from DrvVD to VBoxHDD where we can determine the type of the operation (read/write)

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

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