VirtualBox

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

Last change on this file since 30382 was 30313, checked in by vboxsync, 15 years ago

Storage/VBoxHDD: optimize image cloning, at the moment only for newly created target images without parents

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