VirtualBox

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

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

VBoxHDD: async I/O updates. Mostly complete except for bug fixes

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

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