VirtualBox

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

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

Fix

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

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