VirtualBox

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

Last change on this file since 28855 was 28800, checked in by vboxsync, 14 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

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

© 2023 Oracle
ContactPrivacy policyTerms of Use