VirtualBox

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

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

VBoxHDD/Async: Fix variable initialization, was always 0 before. Fix unallowed modification of the parent S/G buffer under certain circumstances. Both bugs happen only during a write to a so far unallocted block

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