VirtualBox

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

Last change on this file since 29603 was 29497, checked in by vboxsync, 15 years ago

Storage/VBoxHDD: fix long-standing bugs in VDCopy, which show in VBoxManage clonehd. Check whether CreateDiff or CreateBase needs to be used was wrong, and copying of parent UUID information is completely useless (the latter is #6408)

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

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