VirtualBox

source: vbox/trunk/src/VBox/Storage/VD.cpp@ 33889

Last change on this file since 33889 was 33745, checked in by vboxsync, 14 years ago

VCI: Updates

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 289.3 KB
Line 
1/* $Id: VD.cpp 33745 2010-11-03 18:31:53Z vboxsync $ */
2/** @file
3 * VBoxHDD - VBox HDD Container implementation.
4 */
5
6/*
7 * Copyright (C) 2006-2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_VD
22#include <VBox/vd.h>
23#include <VBox/err.h>
24#include <VBox/sup.h>
25#include <VBox/log.h>
26
27#include <iprt/alloc.h>
28#include <iprt/assert.h>
29#include <iprt/uuid.h>
30#include <iprt/file.h>
31#include <iprt/string.h>
32#include <iprt/asm.h>
33#include <iprt/ldr.h>
34#include <iprt/dir.h>
35#include <iprt/path.h>
36#include <iprt/param.h>
37#include <iprt/memcache.h>
38#include <iprt/sg.h>
39#include <iprt/critsect.h>
40#include <iprt/list.h>
41#include <iprt/avl.h>
42
43#include <VBox/vd-plugin.h>
44#include <VBox/vd-cache-plugin.h>
45
46#define VBOXHDDDISK_SIGNATURE 0x6f0e2a7d
47
48/** Buffer size used for merging images. */
49#define VD_MERGE_BUFFER_SIZE (16 * _1M)
50
51/** Maximum number of segments in one I/O task. */
52#define VD_IO_TASK_SEGMENTS_MAX 64
53
54/**
55 * VD async I/O interface storage descriptor.
56 */
57typedef struct VDIIOFALLBACKSTORAGE
58{
59 /** File handle. */
60 RTFILE File;
61 /** Completion callback. */
62 PFNVDCOMPLETED pfnCompleted;
63 /** Thread for async access. */
64 RTTHREAD ThreadAsync;
65} VDIIOFALLBACKSTORAGE, *PVDIIOFALLBACKSTORAGE;
66
67/**
68 * Structure containing everything I/O related
69 * for the image and cache descriptors.
70 */
71typedef struct VDIO
72{
73 /** I/O interface to the upper layer. */
74 PVDINTERFACE pInterfaceIO;
75 /** I/O interface callback table. */
76 PVDINTERFACEIO pInterfaceIOCallbacks;
77
78 /** Per image internal I/O interface. */
79 VDINTERFACE VDIIOInt;
80
81 /** Fallback I/O interface, only used if the caller doesn't provide it. */
82 VDINTERFACE VDIIO;
83
84 /** Opaque backend data. */
85 void *pBackendData;
86 /** Disk this image is part of */
87 PVBOXHDD pDisk;
88} VDIO, *PVDIO;
89
90/**
91 * VBox HDD Container image descriptor.
92 */
93typedef struct VDIMAGE
94{
95 /** Link to parent image descriptor, if any. */
96 struct VDIMAGE *pPrev;
97 /** Link to child image descriptor, if any. */
98 struct VDIMAGE *pNext;
99 /** Container base filename. (UTF-8) */
100 char *pszFilename;
101 /** Data managed by the backend which keeps the actual info. */
102 void *pBackendData;
103 /** Cached sanitized image flags. */
104 unsigned uImageFlags;
105 /** Image open flags (only those handled generically in this code and which
106 * the backends will never ever see). */
107 unsigned uOpenFlags;
108
109 /** Function pointers for the various backend methods. */
110 PCVBOXHDDBACKEND Backend;
111 /** Pointer to list of VD interfaces, per-image. */
112 PVDINTERFACE pVDIfsImage;
113 /** I/O related things. */
114 VDIO VDIo;
115} VDIMAGE, *PVDIMAGE;
116
117/**
118 * uModified bit flags.
119 */
120#define VD_IMAGE_MODIFIED_FLAG RT_BIT(0)
121#define VD_IMAGE_MODIFIED_FIRST RT_BIT(1)
122#define VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE RT_BIT(2)
123
124
125/**
126 * VBox HDD Cache image descriptor.
127 */
128typedef struct VDCACHE
129{
130 /** Cache base filename. (UTF-8) */
131 char *pszFilename;
132 /** Data managed by the backend which keeps the actual info. */
133 void *pBackendData;
134 /** Cached sanitized image flags. */
135 unsigned uImageFlags;
136 /** Image open flags (only those handled generically in this code and which
137 * the backends will never ever see). */
138 unsigned uOpenFlags;
139
140 /** Function pointers for the various backend methods. */
141 PCVDCACHEBACKEND Backend;
142
143 /** Pointer to list of VD interfaces, per-cache. */
144 PVDINTERFACE pVDIfsCache;
145 /** I/O related things. */
146 VDIO VDIo;
147} VDCACHE, *PVDCACHE;
148
149/**
150 * VBox HDD Container main structure, private part.
151 */
152struct VBOXHDD
153{
154 /** Structure signature (VBOXHDDDISK_SIGNATURE). */
155 uint32_t u32Signature;
156
157 /** Image type. */
158 VDTYPE enmType;
159
160 /** Number of opened images. */
161 unsigned cImages;
162
163 /** Base image. */
164 PVDIMAGE pBase;
165
166 /** Last opened image in the chain.
167 * The same as pBase if only one image is used. */
168 PVDIMAGE pLast;
169
170 /** If a merge to one of the parents is running this may be non-NULL
171 * to indicate to what image the writes should be additionally relayed. */
172 PVDIMAGE pImageRelay;
173
174 /** Flags representing the modification state. */
175 unsigned uModified;
176
177 /** Cached size of this disk. */
178 uint64_t cbSize;
179 /** Cached PCHS geometry for this disk. */
180 VDGEOMETRY PCHSGeometry;
181 /** Cached LCHS geometry for this disk. */
182 VDGEOMETRY LCHSGeometry;
183
184 /** Pointer to list of VD interfaces, per-disk. */
185 PVDINTERFACE pVDIfsDisk;
186 /** Pointer to the common interface structure for error reporting. */
187 PVDINTERFACE pInterfaceError;
188 /** Pointer to the error interface callbacks we use if available. */
189 PVDINTERFACEERROR pInterfaceErrorCallbacks;
190
191 /** Pointer to the optional thread synchronization interface. */
192 PVDINTERFACE pInterfaceThreadSync;
193 /** Pointer to the optional thread synchronization callbacks. */
194 PVDINTERFACETHREADSYNC pInterfaceThreadSyncCallbacks;
195
196 /** Internal I/O interface callback table for the images. */
197 VDINTERFACEIOINT VDIIOIntCallbacks;
198
199 /** Callback table for the fallback I/O interface. */
200 VDINTERFACEIO VDIIOCallbacks;
201
202 /** Memory cache for I/O contexts */
203 RTMEMCACHE hMemCacheIoCtx;
204 /** Memory cache for I/O tasks. */
205 RTMEMCACHE hMemCacheIoTask;
206 /** Critical section protecting the disk against concurrent access. */
207 RTCRITSECT CritSect;
208 /** Flag whether the disk is currently locked by growing write or a flush
209 * request. Other flush or growing write requests need to wait until
210 * the current one completes.
211 */
212 volatile bool fLocked;
213 /** List of waiting requests. - Protected by the critical section. */
214 RTLISTNODE ListWriteLocked;
215
216 /** Pointer to the L2 disk cache if any. */
217 PVDCACHE pCache;
218};
219
220# define VD_THREAD_IS_CRITSECT_OWNER(Disk) \
221 do \
222 { \
223 AssertMsg(RTCritSectIsOwner(&Disk->CritSect), \
224 ("Thread does not own critical section\n"));\
225 } while(0)
226
227/**
228 * VBox parent read descriptor, used internally for compaction.
229 */
230typedef struct VDPARENTSTATEDESC
231{
232 /** Pointer to disk descriptor. */
233 PVBOXHDD pDisk;
234 /** Pointer to image descriptor. */
235 PVDIMAGE pImage;
236} VDPARENTSTATEDESC, *PVDPARENTSTATEDESC;
237
238/**
239 * Transfer direction.
240 */
241typedef enum VDIOCTXTXDIR
242{
243 /** Read */
244 VDIOCTXTXDIR_READ = 0,
245 /** Write */
246 VDIOCTXTXDIR_WRITE,
247 /** Flush */
248 VDIOCTXTXDIR_FLUSH,
249 /** 32bit hack */
250 VDIOCTXTXDIR_32BIT_HACK = 0x7fffffff
251} VDIOCTXTXDIR, *PVDIOCTXTXDIR;
252
253/** Transfer function */
254typedef DECLCALLBACK(int) FNVDIOCTXTRANSFER (PVDIOCTX pIoCtx);
255/** Pointer to a transfer function. */
256typedef FNVDIOCTXTRANSFER *PFNVDIOCTXTRANSFER;
257
258/**
259 * I/O context
260 */
261typedef struct VDIOCTX
262{
263 /** Disk this is request is for. */
264 PVBOXHDD pDisk;
265 /** Return code. */
266 int rcReq;
267 /** Transfer direction */
268 VDIOCTXTXDIR enmTxDir;
269 /** Number of bytes left until this context completes. */
270 volatile uint32_t cbTransferLeft;
271 /** Current offset */
272 volatile uint64_t uOffset;
273 /** Number of bytes to transfer */
274 volatile size_t cbTransfer;
275 /** Current image in the chain. */
276 PVDIMAGE pImage;
277 /** S/G buffer */
278 RTSGBUF SgBuf;
279 /** Flag whether the I/O context is blocked because it is in the growing list. */
280 bool fBlocked;
281 /** Number of data transfers currently pending. */
282 volatile uint32_t cDataTransfersPending;
283 /** How many meta data transfers are pending. */
284 volatile uint32_t cMetaTransfersPending;
285 /** Flag whether the request finished */
286 volatile bool fComplete;
287 /** Temporary allocated memory which is freed
288 * when the context completes. */
289 void *pvAllocation;
290 /** Transfer function. */
291 PFNVDIOCTXTRANSFER pfnIoCtxTransfer;
292 /** Next transfer part after the current one completed. */
293 PFNVDIOCTXTRANSFER pfnIoCtxTransferNext;
294 /** Parent I/O context if any. Sets the type of the context (root/child) */
295 PVDIOCTX pIoCtxParent;
296 /** Type dependent data (root/child) */
297 union
298 {
299 /** Root data */
300 struct
301 {
302 /** Completion callback */
303 PFNVDASYNCTRANSFERCOMPLETE pfnComplete;
304 /** User argument 1 passed on completion. */
305 void *pvUser1;
306 /** User argument 1 passed on completion. */
307 void *pvUser2;
308 } Root;
309 /** Child data */
310 struct
311 {
312 /** Saved start offset */
313 uint64_t uOffsetSaved;
314 /** Saved transfer size */
315 size_t cbTransferLeftSaved;
316 /** Number of bytes transferred from the parent if this context completes. */
317 size_t cbTransferParent;
318 /** Number of bytes to pre read */
319 size_t cbPreRead;
320 /** Number of bytes to post read. */
321 size_t cbPostRead;
322 /** Number of bytes to write left in the parent. */
323 size_t cbWriteParent;
324 /** Write type dependent data. */
325 union
326 {
327 /** Optimized */
328 struct
329 {
330 /** Bytes to fill to satisfy the block size. Not part of the virtual disk. */
331 size_t cbFill;
332 /** Bytes to copy instead of reading from the parent */
333 size_t cbWriteCopy;
334 /** Bytes to read from the image. */
335 size_t cbReadImage;
336 } Optimized;
337 } Write;
338 } Child;
339 } Type;
340} VDIOCTX;
341
342typedef struct VDIOCTXDEFERRED
343{
344 /** Node in the list of deferred requests.
345 * A request can be deferred if the image is growing
346 * and the request accesses the same range or if
347 * the backend needs to read or write metadata from the disk
348 * before it can continue. */
349 RTLISTNODE NodeDeferred;
350 /** I/O context this entry points to. */
351 PVDIOCTX pIoCtx;
352} VDIOCTXDEFERRED, *PVDIOCTXDEFERRED;
353
354/**
355 * I/O task.
356 */
357typedef struct VDIOTASK
358{
359 /** Storage this task belongs to. */
360 PVDIOSTORAGE pIoStorage;
361 /** Optional completion callback. */
362 PFNVDXFERCOMPLETED pfnComplete;
363 /** Opaque user data. */
364 void *pvUser;
365 /** Flag whether this is a meta data transfer. */
366 bool fMeta;
367 /** Type dependent data. */
368 union
369 {
370 /** User data transfer. */
371 struct
372 {
373 /** Number of bytes this task transferred. */
374 uint32_t cbTransfer;
375 /** Pointer to the I/O context the task belongs. */
376 PVDIOCTX pIoCtx;
377 } User;
378 /** Meta data transfer. */
379 struct
380 {
381 /** Meta transfer this task is for. */
382 PVDMETAXFER pMetaXfer;
383 } Meta;
384 } Type;
385} VDIOTASK, *PVDIOTASK;
386
387/**
388 * Storage handle.
389 */
390typedef struct VDIOSTORAGE
391{
392 /** Image I/O state this storage handle belongs to. */
393 PVDIO pVDIo;
394 /** AVL tree for pending async metadata transfers. */
395 PAVLRFOFFTREE pTreeMetaXfers;
396 /** Storage handle */
397 void *pStorage;
398} VDIOSTORAGE;
399
400/**
401 * Metadata transfer.
402 *
403 * @note This entry can't be freed if either the list is not empty or
404 * the reference counter is not 0.
405 * The assumption is that the backends don't need to read huge amounts of
406 * metadata to complete a transfer so the additional memory overhead should
407 * be relatively small.
408 */
409typedef struct VDMETAXFER
410{
411 /** AVL core for fast search (the file offset is the key) */
412 AVLRFOFFNODECORE Core;
413 /** I/O storage for this transfer. */
414 PVDIOSTORAGE pIoStorage;
415 /** Flags. */
416 uint32_t fFlags;
417 /** List of I/O contexts waiting for this metadata transfer to complete. */
418 RTLISTNODE ListIoCtxWaiting;
419 /** Number of references to this entry. */
420 unsigned cRefs;
421 /** Size of the data stored with this entry. */
422 size_t cbMeta;
423 /** Data stored - variable size. */
424 uint8_t abData[1];
425} VDMETAXFER;
426
427/**
428 * The transfer direction for the metadata.
429 */
430#define VDMETAXFER_TXDIR_MASK 0x3
431#define VDMETAXFER_TXDIR_NONE 0x0
432#define VDMETAXFER_TXDIR_WRITE 0x1
433#define VDMETAXFER_TXDIR_READ 0x2
434#define VDMETAXFER_TXDIR_FLUSH 0x3
435#define VDMETAXFER_TXDIR_GET(flags) ((flags) & VDMETAXFER_TXDIR_MASK)
436#define VDMETAXFER_TXDIR_SET(flags, dir) ((flags) = (flags & ~VDMETAXFER_TXDIR_MASK) | (dir))
437
438extern VBOXHDDBACKEND g_RawBackend;
439extern VBOXHDDBACKEND g_VmdkBackend;
440extern VBOXHDDBACKEND g_VDIBackend;
441extern VBOXHDDBACKEND g_VhdBackend;
442extern VBOXHDDBACKEND g_ParallelsBackend;
443extern VBOXHDDBACKEND g_DmgBackend;
444extern VBOXHDDBACKEND g_ISCSIBackend;
445
446static unsigned g_cBackends = 0;
447static PVBOXHDDBACKEND *g_apBackends = NULL;
448static PVBOXHDDBACKEND aStaticBackends[] =
449{
450 &g_VmdkBackend,
451 &g_VDIBackend,
452 &g_VhdBackend,
453 &g_ParallelsBackend,
454 &g_DmgBackend,
455 &g_RawBackend,
456 &g_ISCSIBackend
457};
458
459/**
460 * Supported backends for the disk cache.
461 */
462extern VDCACHEBACKEND g_VciCacheBackend;
463
464static unsigned g_cCacheBackends = 0;
465static PVDCACHEBACKEND *g_apCacheBackends = NULL;
466static PVDCACHEBACKEND aStaticCacheBackends[] =
467{
468 &g_VciCacheBackend
469};
470
471/**
472 * internal: add several backends.
473 */
474static int vdAddBackends(PVBOXHDDBACKEND *ppBackends, unsigned cBackends)
475{
476 PVBOXHDDBACKEND *pTmp = (PVBOXHDDBACKEND*)RTMemRealloc(g_apBackends,
477 (g_cBackends + cBackends) * sizeof(PVBOXHDDBACKEND));
478 if (RT_UNLIKELY(!pTmp))
479 return VERR_NO_MEMORY;
480 g_apBackends = pTmp;
481 memcpy(&g_apBackends[g_cBackends], ppBackends, cBackends * sizeof(PVBOXHDDBACKEND));
482 g_cBackends += cBackends;
483 return VINF_SUCCESS;
484}
485
486/**
487 * internal: add single backend.
488 */
489DECLINLINE(int) vdAddBackend(PVBOXHDDBACKEND pBackend)
490{
491 return vdAddBackends(&pBackend, 1);
492}
493
494/**
495 * internal: add several cache backends.
496 */
497static int vdAddCacheBackends(PVDCACHEBACKEND *ppBackends, unsigned cBackends)
498{
499 PVDCACHEBACKEND *pTmp = (PVDCACHEBACKEND*)RTMemRealloc(g_apCacheBackends,
500 (g_cCacheBackends + cBackends) * sizeof(PVDCACHEBACKEND));
501 if (RT_UNLIKELY(!pTmp))
502 return VERR_NO_MEMORY;
503 g_apCacheBackends = pTmp;
504 memcpy(&g_apCacheBackends[g_cCacheBackends], ppBackends, cBackends * sizeof(PVDCACHEBACKEND));
505 g_cCacheBackends += cBackends;
506 return VINF_SUCCESS;
507}
508
509/**
510 * internal: add single cache backend.
511 */
512DECLINLINE(int) vdAddCacheBackend(PVDCACHEBACKEND pBackend)
513{
514 return vdAddCacheBackends(&pBackend, 1);
515}
516
517/**
518 * internal: issue error message.
519 */
520static int vdError(PVBOXHDD pDisk, int rc, RT_SRC_POS_DECL,
521 const char *pszFormat, ...)
522{
523 va_list va;
524 va_start(va, pszFormat);
525 if (pDisk->pInterfaceErrorCallbacks)
526 pDisk->pInterfaceErrorCallbacks->pfnError(pDisk->pInterfaceError->pvUser, rc, RT_SRC_POS_ARGS, pszFormat, va);
527 va_end(va);
528 return rc;
529}
530
531/**
532 * internal: thread synchronization, start read.
533 */
534DECLINLINE(int) vdThreadStartRead(PVBOXHDD pDisk)
535{
536 int rc = VINF_SUCCESS;
537 if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
538 rc = pDisk->pInterfaceThreadSyncCallbacks->pfnStartRead(pDisk->pInterfaceThreadSync->pvUser);
539 return rc;
540}
541
542/**
543 * internal: thread synchronization, finish read.
544 */
545DECLINLINE(int) vdThreadFinishRead(PVBOXHDD pDisk)
546{
547 int rc = VINF_SUCCESS;
548 if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
549 rc = pDisk->pInterfaceThreadSyncCallbacks->pfnFinishRead(pDisk->pInterfaceThreadSync->pvUser);
550 return rc;
551}
552
553/**
554 * internal: thread synchronization, start write.
555 */
556DECLINLINE(int) vdThreadStartWrite(PVBOXHDD pDisk)
557{
558 int rc = VINF_SUCCESS;
559 if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
560 rc = pDisk->pInterfaceThreadSyncCallbacks->pfnStartWrite(pDisk->pInterfaceThreadSync->pvUser);
561 return rc;
562}
563
564/**
565 * internal: thread synchronization, finish write.
566 */
567DECLINLINE(int) vdThreadFinishWrite(PVBOXHDD pDisk)
568{
569 int rc = VINF_SUCCESS;
570 if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
571 rc = pDisk->pInterfaceThreadSyncCallbacks->pfnFinishWrite(pDisk->pInterfaceThreadSync->pvUser);
572 return rc;
573}
574
575/**
576 * internal: find image format backend.
577 */
578static int vdFindBackend(const char *pszBackend, PCVBOXHDDBACKEND *ppBackend)
579{
580 int rc = VINF_SUCCESS;
581 PCVBOXHDDBACKEND pBackend = NULL;
582
583 if (!g_apBackends)
584 VDInit();
585
586 for (unsigned i = 0; i < g_cBackends; i++)
587 {
588 if (!RTStrICmp(pszBackend, g_apBackends[i]->pszBackendName))
589 {
590 pBackend = g_apBackends[i];
591 break;
592 }
593 }
594 *ppBackend = pBackend;
595 return rc;
596}
597
598/**
599 * internal: find cache format backend.
600 */
601static int vdFindCacheBackend(const char *pszBackend, PCVDCACHEBACKEND *ppBackend)
602{
603 int rc = VINF_SUCCESS;
604 PCVDCACHEBACKEND pBackend = NULL;
605
606 if (!g_apCacheBackends)
607 VDInit();
608
609 for (unsigned i = 0; i < g_cCacheBackends; i++)
610 {
611 if (!RTStrICmp(pszBackend, g_apCacheBackends[i]->pszBackendName))
612 {
613 pBackend = g_apCacheBackends[i];
614 break;
615 }
616 }
617 *ppBackend = pBackend;
618 return rc;
619}
620
621/**
622 * internal: add image structure to the end of images list.
623 */
624static void vdAddImageToList(PVBOXHDD pDisk, PVDIMAGE pImage)
625{
626 pImage->pPrev = NULL;
627 pImage->pNext = NULL;
628
629 if (pDisk->pBase)
630 {
631 Assert(pDisk->cImages > 0);
632 pImage->pPrev = pDisk->pLast;
633 pDisk->pLast->pNext = pImage;
634 pDisk->pLast = pImage;
635 }
636 else
637 {
638 Assert(pDisk->cImages == 0);
639 pDisk->pBase = pImage;
640 pDisk->pLast = pImage;
641 }
642
643 pDisk->cImages++;
644}
645
646/**
647 * internal: remove image structure from the images list.
648 */
649static void vdRemoveImageFromList(PVBOXHDD pDisk, PVDIMAGE pImage)
650{
651 Assert(pDisk->cImages > 0);
652
653 if (pImage->pPrev)
654 pImage->pPrev->pNext = pImage->pNext;
655 else
656 pDisk->pBase = pImage->pNext;
657
658 if (pImage->pNext)
659 pImage->pNext->pPrev = pImage->pPrev;
660 else
661 pDisk->pLast = pImage->pPrev;
662
663 pImage->pPrev = NULL;
664 pImage->pNext = NULL;
665
666 pDisk->cImages--;
667}
668
669/**
670 * internal: find image by index into the images list.
671 */
672static PVDIMAGE vdGetImageByNumber(PVBOXHDD pDisk, unsigned nImage)
673{
674 PVDIMAGE pImage = pDisk->pBase;
675 if (nImage == VD_LAST_IMAGE)
676 return pDisk->pLast;
677 while (pImage && nImage)
678 {
679 pImage = pImage->pNext;
680 nImage--;
681 }
682 return pImage;
683}
684
685/**
686 * Internal: Tries to read the desired range from the given cache.
687 *
688 * @returns VBox status code.
689 * @retval VERR_VD_BLOCK_FREE if the block is not in the cache.
690 * pcbRead will be set to the number of bytes not in the cache.
691 * Everything thereafter might be in the cache.
692 * @param pCache The cache to read from.
693 * @param uOffset Offset of the virtual disk to read.
694 * @param pvBuf Where to store the read data.
695 * @param cbRead How much to read.
696 * @param pcbRead Where to store the number of bytes actually read.
697 * On success this indicates the number of bytes read from the cache.
698 * If VERR_VD_BLOCK_FREE is returned this gives the number of bytes
699 * which are not in the cache.
700 * In both cases everything beyond this value
701 * might or might not be in the cache.
702 */
703static int vdCacheReadHelper(PVDCACHE pCache, uint64_t uOffset,
704 void *pvBuf, size_t cbRead, size_t *pcbRead)
705{
706 int rc = VINF_SUCCESS;
707
708 LogFlowFunc(("pCache=%#p uOffset=%llu pvBuf=%#p cbRead=%zu pcbRead=%#p\n",
709 pCache, uOffset, pvBuf, cbRead, pcbRead));
710
711 AssertPtr(pCache);
712 AssertPtr(pcbRead);
713
714 rc = pCache->Backend->pfnRead(pCache->pBackendData, uOffset, pvBuf,
715 cbRead, pcbRead);
716
717 LogFlowFunc(("returns rc=%Rrc pcbRead=%zu\n", rc, *pcbRead));
718 return rc;
719}
720
721/**
722 * Internal: Writes data for the given block into the cache.
723 *
724 * @returns VBox status code.
725 * @param pCache The cache to write to.
726 * @param uOffset Offset of the virtual disk to write to teh cache.
727 * @param pcvBuf The data to write.
728 * @param cbWrite How much to write.
729 * @param pcbWritten How much data could be written, optional.
730 */
731static int vdCacheWriteHelper(PVDCACHE pCache, uint64_t uOffset, const void *pcvBuf,
732 size_t cbWrite, size_t *pcbWritten)
733{
734 int rc = VINF_SUCCESS;
735
736 LogFlowFunc(("pCache=%#p uOffset=%llu pvBuf=%#p cbWrite=%zu pcbWritten=%#p\n",
737 pCache, uOffset, pcvBuf, cbWrite, pcbWritten));
738
739 AssertPtr(pCache);
740 AssertPtr(pcvBuf);
741 Assert(cbWrite > 0);
742
743 if (pcbWritten)
744 rc = pCache->Backend->pfnWrite(pCache->pBackendData, uOffset, pcvBuf,
745 cbWrite, pcbWritten);
746 else
747 {
748 size_t cbWritten = 0;
749
750 do
751 {
752 rc = pCache->Backend->pfnWrite(pCache->pBackendData, uOffset, pcvBuf,
753 cbWrite, &cbWritten);
754 uOffset += cbWritten;
755 pcvBuf = (char *)pcvBuf + cbWritten;
756 cbWrite -= cbWritten;
757 } while ( cbWrite
758 && RT_SUCCESS(rc));
759 }
760
761 LogFlowFunc(("returns rc=%Rrc pcbWritten=%zu\n",
762 rc, pcbWritten ? *pcbWritten : cbWrite));
763 return rc;
764}
765
766/**
767 * Internal: Reads a given amount of data from the image chain of the disk.
768 **/
769static int vdDiskReadHelper(PVBOXHDD pDisk, PVDIMAGE pImage, PVDIMAGE pImageParentOverride,
770 uint64_t uOffset, void *pvBuf, size_t cbRead, size_t *pcbThisRead)
771{
772 int rc = VINF_SUCCESS;
773 size_t cbThisRead = cbRead;
774
775 AssertPtr(pcbThisRead);
776
777 *pcbThisRead = 0;
778
779 /*
780 * Try to read from the given image.
781 * If the block is not allocated read from override chain if present.
782 */
783 rc = pImage->Backend->pfnRead(pImage->pBackendData,
784 uOffset, pvBuf, cbThisRead,
785 &cbThisRead);
786
787 if (rc == VERR_VD_BLOCK_FREE)
788 {
789 for (PVDIMAGE pCurrImage = pImageParentOverride ? pImageParentOverride : pImage->pPrev;
790 pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
791 pCurrImage = pCurrImage->pPrev)
792 {
793 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
794 uOffset, pvBuf, cbThisRead,
795 &cbThisRead);
796 }
797 }
798
799 if (RT_SUCCESS(rc) || rc == VERR_VD_BLOCK_FREE)
800 *pcbThisRead = cbThisRead;
801
802 return rc;
803}
804
805/**
806 * internal: read the specified amount of data in whatever blocks the backend
807 * will give us.
808 */
809static int vdReadHelper(PVBOXHDD pDisk, PVDIMAGE pImage, PVDIMAGE pImageParentOverride,
810 uint64_t uOffset, void *pvBuf, size_t cbRead,
811 bool fZeroFreeBlocks, bool fUpdateCache)
812{
813 int rc = VINF_SUCCESS;
814 size_t cbThisRead;
815 bool fAllFree = true;
816 size_t cbBufClear = 0;
817
818 /* Loop until all read. */
819 do
820 {
821 /* Search for image with allocated block. Do not attempt to read more
822 * than the previous reads marked as valid. Otherwise this would return
823 * stale data when different block sizes are used for the images. */
824 cbThisRead = cbRead;
825
826 if ( pDisk->pCache
827 && !pImageParentOverride)
828 {
829 rc = vdCacheReadHelper(pDisk->pCache, uOffset, pvBuf,
830 cbThisRead, &cbThisRead);
831
832 if (rc == VERR_VD_BLOCK_FREE)
833 {
834 rc = vdDiskReadHelper(pDisk, pImage, pImageParentOverride,
835 uOffset, pvBuf, cbThisRead, &cbThisRead);
836
837 /* If the read was successful, write the data back into the cache. */
838 if ( RT_SUCCESS(rc)
839 && fUpdateCache)
840 {
841 rc = vdCacheWriteHelper(pDisk->pCache, uOffset, pvBuf,
842 cbThisRead, NULL);
843 }
844 }
845 }
846 else
847 {
848 /** @todo can be be replaced by vdDiskReadHelper if it proves to be reliable,
849 * don't want to be responsible for data corruption...
850 */
851 /*
852 * Try to read from the given image.
853 * If the block is not allocated read from override chain if present.
854 */
855 rc = pImage->Backend->pfnRead(pImage->pBackendData,
856 uOffset, pvBuf, cbThisRead,
857 &cbThisRead);
858
859 if (rc == VERR_VD_BLOCK_FREE)
860 {
861 for (PVDIMAGE pCurrImage = pImageParentOverride ? pImageParentOverride : pImage->pPrev;
862 pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
863 pCurrImage = pCurrImage->pPrev)
864 {
865 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
866 uOffset, pvBuf, cbThisRead,
867 &cbThisRead);
868 }
869 }
870 }
871
872 /* No image in the chain contains the data for the block. */
873 if (rc == VERR_VD_BLOCK_FREE)
874 {
875 /* Fill the free space with 0 if we are told to do so
876 * or a previous read returned valid data. */
877 if (fZeroFreeBlocks || !fAllFree)
878 memset(pvBuf, '\0', cbThisRead);
879 else
880 cbBufClear += cbThisRead;
881
882 rc = VINF_SUCCESS;
883 }
884 else if (RT_SUCCESS(rc))
885 {
886 /* First not free block, fill the space before with 0. */
887 if (!fZeroFreeBlocks)
888 {
889 memset((char *)pvBuf - cbBufClear, '\0', cbBufClear);
890 cbBufClear = 0;
891 fAllFree = false;
892 }
893 }
894
895 cbRead -= cbThisRead;
896 uOffset += cbThisRead;
897 pvBuf = (char *)pvBuf + cbThisRead;
898 } while (cbRead != 0 && RT_SUCCESS(rc));
899
900 return (!fZeroFreeBlocks && fAllFree) ? VERR_VD_BLOCK_FREE : rc;
901}
902
903DECLINLINE(PVDIOCTX) vdIoCtxAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
904 uint64_t uOffset, size_t cbTransfer,
905 PCRTSGSEG pcaSeg, unsigned cSeg,
906 void *pvAllocation,
907 PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
908{
909 PVDIOCTX pIoCtx = NULL;
910
911 pIoCtx = (PVDIOCTX)RTMemCacheAlloc(pDisk->hMemCacheIoCtx);
912 if (RT_LIKELY(pIoCtx))
913 {
914 pIoCtx->pDisk = pDisk;
915 pIoCtx->enmTxDir = enmTxDir;
916 pIoCtx->cbTransferLeft = cbTransfer;
917 pIoCtx->uOffset = uOffset;
918 pIoCtx->cbTransfer = cbTransfer;
919 pIoCtx->cDataTransfersPending = 0;
920 pIoCtx->cMetaTransfersPending = 0;
921 pIoCtx->fComplete = false;
922 pIoCtx->fBlocked = false;
923 pIoCtx->pvAllocation = pvAllocation;
924 pIoCtx->pfnIoCtxTransfer = pfnIoCtxTransfer;
925 pIoCtx->pfnIoCtxTransferNext = NULL;
926 pIoCtx->rcReq = VINF_SUCCESS;
927
928 /* There is no S/G list for a flush request. */
929 if (enmTxDir != VDIOCTXTXDIR_FLUSH)
930 RTSgBufInit(&pIoCtx->SgBuf, pcaSeg, cSeg);
931 else
932 memset(&pIoCtx->SgBuf, 0, sizeof(RTSGBUF));
933 }
934
935 return pIoCtx;
936}
937
938DECLINLINE(PVDIOCTX) vdIoCtxRootAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
939 uint64_t uOffset, size_t cbTransfer,
940 PCRTSGSEG paSeg, unsigned cSeg,
941 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
942 void *pvUser1, void *pvUser2,
943 void *pvAllocation,
944 PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
945{
946 PVDIOCTX pIoCtx = vdIoCtxAlloc(pDisk, enmTxDir, uOffset, cbTransfer,
947 paSeg, cSeg, pvAllocation, pfnIoCtxTransfer);
948
949 if (RT_LIKELY(pIoCtx))
950 {
951 pIoCtx->pIoCtxParent = NULL;
952 pIoCtx->Type.Root.pfnComplete = pfnComplete;
953 pIoCtx->Type.Root.pvUser1 = pvUser1;
954 pIoCtx->Type.Root.pvUser2 = pvUser2;
955 }
956
957 LogFlow(("Allocated root I/O context %#p\n", pIoCtx));
958 return pIoCtx;
959}
960
961DECLINLINE(PVDIOCTX) vdIoCtxChildAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
962 uint64_t uOffset, size_t cbTransfer,
963 PCRTSGSEG paSeg, unsigned cSeg,
964 PVDIOCTX pIoCtxParent, size_t cbTransferParent,
965 size_t cbWriteParent, void *pvAllocation,
966 PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
967{
968 PVDIOCTX pIoCtx = vdIoCtxAlloc(pDisk, enmTxDir, uOffset, cbTransfer,
969 paSeg, cSeg, pvAllocation, pfnIoCtxTransfer);
970
971 AssertPtr(pIoCtxParent);
972 Assert(!pIoCtxParent->pIoCtxParent);
973
974 if (RT_LIKELY(pIoCtx))
975 {
976 pIoCtx->pIoCtxParent = pIoCtxParent;
977 pIoCtx->Type.Child.uOffsetSaved = uOffset;
978 pIoCtx->Type.Child.cbTransferLeftSaved = cbTransfer;
979 pIoCtx->Type.Child.cbTransferParent = cbTransferParent;
980 pIoCtx->Type.Child.cbWriteParent = cbWriteParent;
981 }
982
983 LogFlow(("Allocated child I/O context %#p\n", pIoCtx));
984 return pIoCtx;
985}
986
987DECLINLINE(PVDIOTASK) vdIoTaskUserAlloc(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnComplete, void *pvUser, PVDIOCTX pIoCtx, uint32_t cbTransfer)
988{
989 PVDIOTASK pIoTask = NULL;
990
991 pIoTask = (PVDIOTASK)RTMemCacheAlloc(pIoStorage->pVDIo->pDisk->hMemCacheIoTask);
992 if (pIoTask)
993 {
994 pIoTask->pIoStorage = pIoStorage;
995 pIoTask->pfnComplete = pfnComplete;
996 pIoTask->pvUser = pvUser;
997 pIoTask->fMeta = false;
998 pIoTask->Type.User.cbTransfer = cbTransfer;
999 pIoTask->Type.User.pIoCtx = pIoCtx;
1000 }
1001
1002 return pIoTask;
1003}
1004
1005DECLINLINE(PVDIOTASK) vdIoTaskMetaAlloc(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnComplete, void *pvUser, PVDMETAXFER pMetaXfer)
1006{
1007 PVDIOTASK pIoTask = NULL;
1008
1009 pIoTask = (PVDIOTASK)RTMemCacheAlloc(pIoStorage->pVDIo->pDisk->hMemCacheIoTask);
1010 if (pIoTask)
1011 {
1012 pIoTask->pIoStorage = pIoStorage;
1013 pIoTask->pfnComplete = pfnComplete;
1014 pIoTask->pvUser = pvUser;
1015 pIoTask->fMeta = true;
1016 pIoTask->Type.Meta.pMetaXfer = pMetaXfer;
1017 }
1018
1019 return pIoTask;
1020}
1021
1022DECLINLINE(void) vdIoCtxFree(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
1023{
1024 LogFlow(("Freeing I/O context %#p\n", pIoCtx));
1025 if (pIoCtx->pvAllocation)
1026 RTMemFree(pIoCtx->pvAllocation);
1027#ifdef DEBUG
1028 memset(pIoCtx, 0xff, sizeof(VDIOCTX));
1029#endif
1030 RTMemCacheFree(pDisk->hMemCacheIoCtx, pIoCtx);
1031}
1032
1033DECLINLINE(void) vdIoTaskFree(PVBOXHDD pDisk, PVDIOTASK pIoTask)
1034{
1035 RTMemCacheFree(pDisk->hMemCacheIoTask, pIoTask);
1036}
1037
1038DECLINLINE(void) vdIoCtxChildReset(PVDIOCTX pIoCtx)
1039{
1040 AssertPtr(pIoCtx->pIoCtxParent);
1041
1042 RTSgBufReset(&pIoCtx->SgBuf);
1043 pIoCtx->uOffset = pIoCtx->Type.Child.uOffsetSaved;
1044 pIoCtx->cbTransferLeft = pIoCtx->Type.Child.cbTransferLeftSaved;
1045}
1046
1047DECLINLINE(PVDMETAXFER) vdMetaXferAlloc(PVDIOSTORAGE pIoStorage, uint64_t uOffset, size_t cb)
1048{
1049 PVDMETAXFER pMetaXfer = (PVDMETAXFER)RTMemAlloc(RT_OFFSETOF(VDMETAXFER, abData[cb]));
1050
1051 if (RT_LIKELY(pMetaXfer))
1052 {
1053 pMetaXfer->Core.Key = uOffset;
1054 pMetaXfer->Core.KeyLast = uOffset + cb - 1;
1055 pMetaXfer->fFlags = VDMETAXFER_TXDIR_NONE;
1056 pMetaXfer->cbMeta = cb;
1057 pMetaXfer->pIoStorage = pIoStorage;
1058 pMetaXfer->cRefs = 0;
1059 RTListInit(&pMetaXfer->ListIoCtxWaiting);
1060 }
1061 return pMetaXfer;
1062}
1063
1064DECLINLINE(int) vdIoCtxDefer(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
1065{
1066 PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
1067
1068 if (!pDeferred)
1069 return VERR_NO_MEMORY;
1070
1071 LogFlowFunc(("Deferring write pIoCtx=%#p\n", pIoCtx));
1072
1073 Assert(!pIoCtx->pIoCtxParent && !pIoCtx->fBlocked);
1074
1075 RTListInit(&pDeferred->NodeDeferred);
1076 pDeferred->pIoCtx = pIoCtx;
1077 RTListAppend(&pDisk->ListWriteLocked, &pDeferred->NodeDeferred);
1078 pIoCtx->fBlocked = true;
1079 return VINF_SUCCESS;
1080}
1081
1082static size_t vdIoCtxCopy(PVDIOCTX pIoCtxDst, PVDIOCTX pIoCtxSrc, size_t cbData)
1083{
1084 return RTSgBufCopy(&pIoCtxDst->SgBuf, &pIoCtxSrc->SgBuf, cbData);
1085}
1086
1087static int vdIoCtxCmp(PVDIOCTX pIoCtx1, PVDIOCTX pIoCtx2, size_t cbData)
1088{
1089 return RTSgBufCmp(&pIoCtx1->SgBuf, &pIoCtx2->SgBuf, cbData);
1090}
1091
1092static size_t vdIoCtxCopyTo(PVDIOCTX pIoCtx, uint8_t *pbData, size_t cbData)
1093{
1094 return RTSgBufCopyToBuf(&pIoCtx->SgBuf, pbData, cbData);
1095}
1096
1097
1098static size_t vdIoCtxCopyFrom(PVDIOCTX pIoCtx, uint8_t *pbData, size_t cbData)
1099{
1100 return RTSgBufCopyFromBuf(&pIoCtx->SgBuf, pbData, cbData);
1101}
1102
1103static size_t vdIoCtxSet(PVDIOCTX pIoCtx, uint8_t ch, size_t cbData)
1104{
1105 return RTSgBufSet(&pIoCtx->SgBuf, ch, cbData);
1106}
1107
1108static int vdIoCtxProcess(PVDIOCTX pIoCtx)
1109{
1110 int rc = VINF_SUCCESS;
1111 PVBOXHDD pDisk = pIoCtx->pDisk;
1112
1113 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
1114
1115 RTCritSectEnter(&pDisk->CritSect);
1116
1117 if ( !pIoCtx->cbTransferLeft
1118 && !pIoCtx->cMetaTransfersPending
1119 && !pIoCtx->cDataTransfersPending
1120 && !pIoCtx->pfnIoCtxTransfer)
1121 {
1122 rc = VINF_VD_ASYNC_IO_FINISHED;
1123 goto out;
1124 }
1125
1126 /*
1127 * We complete the I/O context in case of an error
1128 * if there is no I/O task pending.
1129 */
1130 if ( RT_FAILURE(pIoCtx->rcReq)
1131 && !pIoCtx->cMetaTransfersPending
1132 && !pIoCtx->cDataTransfersPending)
1133 {
1134 rc = VINF_VD_ASYNC_IO_FINISHED;
1135 goto out;
1136 }
1137
1138 /* Don't change anything if there is a metadata transfer pending or we are blocked. */
1139 if ( pIoCtx->cMetaTransfersPending
1140 || pIoCtx->fBlocked)
1141 {
1142 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1143 goto out;
1144 }
1145
1146 if (pIoCtx->pfnIoCtxTransfer)
1147 {
1148 /* Call the transfer function advancing to the next while there is no error. */
1149 while ( pIoCtx->pfnIoCtxTransfer
1150 && RT_SUCCESS(rc))
1151 {
1152 LogFlowFunc(("calling transfer function %#p\n", pIoCtx->pfnIoCtxTransfer));
1153 rc = pIoCtx->pfnIoCtxTransfer(pIoCtx);
1154
1155 /* Advance to the next part of the transfer if the current one succeeded. */
1156 if (RT_SUCCESS(rc))
1157 {
1158 pIoCtx->pfnIoCtxTransfer = pIoCtx->pfnIoCtxTransferNext;
1159 pIoCtx->pfnIoCtxTransferNext = NULL;
1160 }
1161 }
1162 }
1163
1164 if ( RT_SUCCESS(rc)
1165 && !pIoCtx->cbTransferLeft
1166 && !pIoCtx->cMetaTransfersPending
1167 && !pIoCtx->cDataTransfersPending)
1168 rc = VINF_VD_ASYNC_IO_FINISHED;
1169 else if ( RT_SUCCESS(rc)
1170 || rc == VERR_VD_NOT_ENOUGH_METADATA
1171 || rc == VERR_VD_IOCTX_HALT)
1172 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1173 else if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
1174 {
1175 ASMAtomicCmpXchgS32(&pIoCtx->rcReq, rc, VINF_SUCCESS);
1176 /*
1177 * The I/O context completed if we have an error and there is no data
1178 * or meta data transfer pending.
1179 */
1180 if ( !pIoCtx->cMetaTransfersPending
1181 && !pIoCtx->cDataTransfersPending)
1182 rc = VINF_VD_ASYNC_IO_FINISHED;
1183 else
1184 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1185 }
1186
1187out:
1188 RTCritSectLeave(&pDisk->CritSect);
1189
1190 LogFlowFunc(("pIoCtx=%#p rc=%Rrc cbTransferLeft=%u cMetaTransfersPending=%u fComplete=%RTbool\n",
1191 pIoCtx, rc, pIoCtx->cbTransferLeft, pIoCtx->cMetaTransfersPending,
1192 pIoCtx->fComplete));
1193
1194 return rc;
1195}
1196
1197static int vdIoCtxLockDisk(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
1198{
1199 int rc = VINF_SUCCESS;
1200
1201 if (!ASMAtomicCmpXchgBool(&pDisk->fLocked, true, false))
1202 {
1203 rc = vdIoCtxDefer(pDisk, pIoCtx);
1204 if (RT_SUCCESS(rc))
1205 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1206 }
1207
1208 return rc;
1209}
1210
1211static void vdIoCtxUnlockDisk(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
1212{
1213 Assert(pDisk->fLocked);
1214 ASMAtomicXchgBool(&pDisk->fLocked, false);
1215
1216 /* Process any pending writes if the current request didn't caused another growing. */
1217 RTCritSectEnter(&pDisk->CritSect);
1218
1219 if (!RTListIsEmpty(&pDisk->ListWriteLocked))
1220 {
1221 RTLISTNODE ListTmp;
1222
1223 RTListMove(&ListTmp, &pDisk->ListWriteLocked);
1224 RTCritSectLeave(&pDisk->CritSect);
1225
1226 /* Process the list. */
1227 do
1228 {
1229 int rc;
1230 PVDIOCTXDEFERRED pDeferred = RTListNodeGetFirst(&ListTmp, VDIOCTXDEFERRED, NodeDeferred);
1231 PVDIOCTX pIoCtxWait = pDeferred->pIoCtx;
1232
1233 AssertPtr(pIoCtxWait);
1234
1235 RTListNodeRemove(&pDeferred->NodeDeferred);
1236 RTMemFree(pDeferred);
1237
1238 Assert(!pIoCtxWait->pIoCtxParent);
1239
1240 pIoCtxWait->fBlocked = false;
1241 LogFlowFunc(("Processing waiting I/O context pIoCtxWait=%#p\n", pIoCtxWait));
1242
1243 rc = vdIoCtxProcess(pIoCtxWait);
1244 if ( rc == VINF_VD_ASYNC_IO_FINISHED
1245 && ASMAtomicCmpXchgBool(&pIoCtxWait->fComplete, true, false))
1246 {
1247 LogFlowFunc(("Waiting I/O context completed pIoCtxWait=%#p\n", pIoCtxWait));
1248 vdThreadFinishWrite(pDisk);
1249 pIoCtxWait->Type.Root.pfnComplete(pIoCtxWait->Type.Root.pvUser1,
1250 pIoCtxWait->Type.Root.pvUser2,
1251 pIoCtxWait->rcReq);
1252 vdIoCtxFree(pDisk, pIoCtxWait);
1253 }
1254 } while (!RTListIsEmpty(&ListTmp));
1255 }
1256 else
1257 RTCritSectLeave(&pDisk->CritSect);
1258}
1259
1260/**
1261 * internal: read the specified amount of data in whatever blocks the backend
1262 * will give us - async version.
1263 */
1264static int vdReadHelperAsync(PVDIOCTX pIoCtx)
1265{
1266 int rc;
1267 size_t cbToRead = pIoCtx->cbTransfer;
1268 uint64_t uOffset = pIoCtx->uOffset;
1269 PVDIMAGE pCurrImage = NULL;
1270 size_t cbThisRead;
1271
1272 /* Loop until all reads started or we have a backend which needs to read metadata. */
1273 do
1274 {
1275 pCurrImage = pIoCtx->pImage;
1276
1277 /* Search for image with allocated block. Do not attempt to read more
1278 * than the previous reads marked as valid. Otherwise this would return
1279 * stale data when different block sizes are used for the images. */
1280 cbThisRead = cbToRead;
1281
1282 /*
1283 * Try to read from the given image.
1284 * If the block is not allocated read from override chain if present.
1285 */
1286 rc = pCurrImage->Backend->pfnAsyncRead(pCurrImage->pBackendData,
1287 uOffset, cbThisRead,
1288 pIoCtx, &cbThisRead);
1289
1290 if (rc == VERR_VD_BLOCK_FREE)
1291 {
1292 for (pCurrImage = pCurrImage->pPrev;
1293 pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
1294 pCurrImage = pCurrImage->pPrev)
1295 {
1296 rc = pCurrImage->Backend->pfnAsyncRead(pCurrImage->pBackendData,
1297 uOffset, cbThisRead,
1298 pIoCtx, &cbThisRead);
1299 }
1300 }
1301
1302 /* The task state will be updated on success already, don't do it here!. */
1303 if (rc == VERR_VD_BLOCK_FREE)
1304 {
1305 /* No image in the chain contains the data for the block. */
1306 vdIoCtxSet(pIoCtx, '\0', cbThisRead);
1307 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbThisRead);
1308 rc = VINF_SUCCESS;
1309 }
1310 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1311 rc = VINF_SUCCESS;
1312 else if (rc == VERR_VD_IOCTX_HALT)
1313 {
1314 uOffset += cbThisRead;
1315 cbToRead -= cbThisRead;
1316 pIoCtx->fBlocked = true;
1317 }
1318
1319 if (RT_FAILURE(rc))
1320 break;
1321
1322 cbToRead -= cbThisRead;
1323 uOffset += cbThisRead;
1324 } while (cbToRead != 0 && RT_SUCCESS(rc));
1325
1326 if ( rc == VERR_VD_NOT_ENOUGH_METADATA
1327 || rc == VERR_VD_IOCTX_HALT)
1328 {
1329 /* Save the current state. */
1330 pIoCtx->uOffset = uOffset;
1331 pIoCtx->cbTransfer = cbToRead;
1332 pIoCtx->pImage = pCurrImage;
1333 }
1334
1335 return rc;
1336}
1337
1338/**
1339 * internal: parent image read wrapper for compacting.
1340 */
1341static int vdParentRead(void *pvUser, uint64_t uOffset, void *pvBuf,
1342 size_t cbRead)
1343{
1344 PVDPARENTSTATEDESC pParentState = (PVDPARENTSTATEDESC)pvUser;
1345 return vdReadHelper(pParentState->pDisk, pParentState->pImage, NULL, uOffset,
1346 pvBuf, cbRead, true /* fZeroFreeBlocks */,
1347 false /* fUpdateCache */);
1348}
1349
1350/**
1351 * internal: mark the disk as not modified.
1352 */
1353static void vdResetModifiedFlag(PVBOXHDD pDisk)
1354{
1355 if (pDisk->uModified & VD_IMAGE_MODIFIED_FLAG)
1356 {
1357 /* generate new last-modified uuid */
1358 if (!(pDisk->uModified & VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
1359 {
1360 RTUUID Uuid;
1361
1362 RTUuidCreate(&Uuid);
1363 pDisk->pLast->Backend->pfnSetModificationUuid(pDisk->pLast->pBackendData,
1364 &Uuid);
1365
1366 if (pDisk->pCache)
1367 pDisk->pCache->Backend->pfnSetModificationUuid(pDisk->pCache->pBackendData,
1368 &Uuid);
1369 }
1370
1371 pDisk->uModified &= ~VD_IMAGE_MODIFIED_FLAG;
1372 }
1373}
1374
1375/**
1376 * internal: mark the disk as modified.
1377 */
1378static void vdSetModifiedFlag(PVBOXHDD pDisk)
1379{
1380 pDisk->uModified |= VD_IMAGE_MODIFIED_FLAG;
1381 if (pDisk->uModified & VD_IMAGE_MODIFIED_FIRST)
1382 {
1383 pDisk->uModified &= ~VD_IMAGE_MODIFIED_FIRST;
1384
1385 /* First modify, so create a UUID and ensure it's written to disk. */
1386 vdResetModifiedFlag(pDisk);
1387
1388 if (!(pDisk->uModified & VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
1389 pDisk->pLast->Backend->pfnFlush(pDisk->pLast->pBackendData);
1390 }
1391}
1392
1393/**
1394 * internal: write a complete block (only used for diff images), taking the
1395 * remaining data from parent images. This implementation does not optimize
1396 * anything (except that it tries to read only that portions from parent
1397 * images that are really needed).
1398 */
1399static int vdWriteHelperStandard(PVBOXHDD pDisk, PVDIMAGE pImage,
1400 PVDIMAGE pImageParentOverride,
1401 uint64_t uOffset, size_t cbWrite,
1402 size_t cbThisWrite, size_t cbPreRead,
1403 size_t cbPostRead, const void *pvBuf,
1404 void *pvTmp)
1405{
1406 int rc = VINF_SUCCESS;
1407
1408 /* Read the data that goes before the write to fill the block. */
1409 if (cbPreRead)
1410 {
1411 /*
1412 * Updating the cache doesn't make sense here because
1413 * this will be done after the complete block was written.
1414 */
1415 rc = vdReadHelper(pDisk, pImage, pImageParentOverride,
1416 uOffset - cbPreRead, pvTmp, cbPreRead,
1417 true /* fZeroFreeBlocks*/,
1418 false /* fUpdateCache */);
1419 if (RT_FAILURE(rc))
1420 return rc;
1421 }
1422
1423 /* Copy the data to the right place in the buffer. */
1424 memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
1425
1426 /* Read the data that goes after the write to fill the block. */
1427 if (cbPostRead)
1428 {
1429 /* If we have data to be written, use that instead of reading
1430 * data from the image. */
1431 size_t cbWriteCopy;
1432 if (cbWrite > cbThisWrite)
1433 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
1434 else
1435 cbWriteCopy = 0;
1436 /* Figure out how much we cannot read from the image, because
1437 * the last block to write might exceed the nominal size of the
1438 * image for technical reasons. */
1439 size_t cbFill;
1440 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
1441 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
1442 else
1443 cbFill = 0;
1444 /* The rest must be read from the image. */
1445 size_t cbReadImage = cbPostRead - cbWriteCopy - cbFill;
1446
1447 /* Now assemble the remaining data. */
1448 if (cbWriteCopy)
1449 memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
1450 (char *)pvBuf + cbThisWrite, cbWriteCopy);
1451 if (cbReadImage)
1452 rc = vdReadHelper(pDisk, pImage, pImageParentOverride,
1453 uOffset + cbThisWrite + cbWriteCopy,
1454 (char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy,
1455 cbReadImage, true /* fZeroFreeBlocks */,
1456 false /* fUpdateCache */);
1457 if (RT_FAILURE(rc))
1458 return rc;
1459 /* Zero out the remainder of this block. Will never be visible, as this
1460 * is beyond the limit of the image. */
1461 if (cbFill)
1462 memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
1463 '\0', cbFill);
1464 }
1465
1466 /* Write the full block to the virtual disk. */
1467 rc = pImage->Backend->pfnWrite(pImage->pBackendData,
1468 uOffset - cbPreRead, pvTmp,
1469 cbPreRead + cbThisWrite + cbPostRead,
1470 NULL, &cbPreRead, &cbPostRead, 0);
1471 Assert(rc != VERR_VD_BLOCK_FREE);
1472 Assert(cbPreRead == 0);
1473 Assert(cbPostRead == 0);
1474
1475 return rc;
1476}
1477
1478/**
1479 * internal: write a complete block (only used for diff images), taking the
1480 * remaining data from parent images. This implementation optimizes out writes
1481 * that do not change the data relative to the state as of the parent images.
1482 * All backends which support differential/growing images support this.
1483 */
1484static int vdWriteHelperOptimized(PVBOXHDD pDisk, PVDIMAGE pImage,
1485 PVDIMAGE pImageParentOverride,
1486 uint64_t uOffset, size_t cbWrite,
1487 size_t cbThisWrite, size_t cbPreRead,
1488 size_t cbPostRead, const void *pvBuf,
1489 void *pvTmp)
1490{
1491 size_t cbFill = 0;
1492 size_t cbWriteCopy = 0;
1493 size_t cbReadImage = 0;
1494 int rc;
1495
1496 if (cbPostRead)
1497 {
1498 /* Figure out how much we cannot read from the image, because
1499 * the last block to write might exceed the nominal size of the
1500 * image for technical reasons. */
1501 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
1502 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
1503
1504 /* If we have data to be written, use that instead of reading
1505 * data from the image. */
1506 if (cbWrite > cbThisWrite)
1507 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
1508
1509 /* The rest must be read from the image. */
1510 cbReadImage = cbPostRead - cbWriteCopy - cbFill;
1511 }
1512
1513 /* Read the entire data of the block so that we can compare whether it will
1514 * be modified by the write or not. */
1515 rc = vdReadHelper(pDisk, pImage, pImageParentOverride, uOffset - cbPreRead, pvTmp,
1516 cbPreRead + cbThisWrite + cbPostRead - cbFill,
1517 true /* fZeroFreeBlocks */,
1518 false /* fUpdateCache */);
1519 if (RT_FAILURE(rc))
1520 return rc;
1521
1522 /* Check if the write would modify anything in this block. */
1523 if ( !memcmp((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite)
1524 && (!cbWriteCopy || !memcmp((char *)pvTmp + cbPreRead + cbThisWrite,
1525 (char *)pvBuf + cbThisWrite, cbWriteCopy)))
1526 {
1527 /* Block is completely unchanged, so no need to write anything. */
1528 return VINF_SUCCESS;
1529 }
1530
1531 /* Copy the data to the right place in the buffer. */
1532 memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
1533
1534 /* Handle the data that goes after the write to fill the block. */
1535 if (cbPostRead)
1536 {
1537 /* Now assemble the remaining data. */
1538 if (cbWriteCopy)
1539 memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
1540 (char *)pvBuf + cbThisWrite, cbWriteCopy);
1541 /* Zero out the remainder of this block. Will never be visible, as this
1542 * is beyond the limit of the image. */
1543 if (cbFill)
1544 memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
1545 '\0', cbFill);
1546 }
1547
1548 /* Write the full block to the virtual disk. */
1549 rc = pImage->Backend->pfnWrite(pImage->pBackendData,
1550 uOffset - cbPreRead, pvTmp,
1551 cbPreRead + cbThisWrite + cbPostRead,
1552 NULL, &cbPreRead, &cbPostRead, 0);
1553 Assert(rc != VERR_VD_BLOCK_FREE);
1554 Assert(cbPreRead == 0);
1555 Assert(cbPostRead == 0);
1556
1557 return rc;
1558}
1559
1560/**
1561 * internal: write buffer to the image, taking care of block boundaries and
1562 * write optimizations.
1563 */
1564static int vdWriteHelper(PVBOXHDD pDisk, PVDIMAGE pImage,
1565 PVDIMAGE pImageParentOverride, uint64_t uOffset,
1566 const void *pvBuf, size_t cbWrite,
1567 bool fUpdateCache)
1568{
1569 int rc;
1570 unsigned fWrite;
1571 size_t cbThisWrite;
1572 size_t cbPreRead, cbPostRead;
1573 uint64_t uOffsetCur = uOffset;
1574 size_t cbWriteCur = cbWrite;
1575 const void *pcvBufCur = pvBuf;
1576
1577 /* Loop until all written. */
1578 do
1579 {
1580 /* Try to write the possibly partial block to the last opened image.
1581 * This works when the block is already allocated in this image or
1582 * if it is a full-block write (and allocation isn't suppressed below).
1583 * For image formats which don't support zero blocks, it's beneficial
1584 * to avoid unnecessarily allocating unchanged blocks. This prevents
1585 * unwanted expanding of images. VMDK is an example. */
1586 cbThisWrite = cbWriteCur;
1587 fWrite = (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
1588 ? 0 : VD_WRITE_NO_ALLOC;
1589 rc = pImage->Backend->pfnWrite(pImage->pBackendData, uOffsetCur, pcvBufCur,
1590 cbThisWrite, &cbThisWrite, &cbPreRead,
1591 &cbPostRead, fWrite);
1592 if (rc == VERR_VD_BLOCK_FREE)
1593 {
1594 void *pvTmp = RTMemTmpAlloc(cbPreRead + cbThisWrite + cbPostRead);
1595 AssertBreakStmt(VALID_PTR(pvTmp), rc = VERR_NO_MEMORY);
1596
1597 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME))
1598 {
1599 /* Optimized write, suppress writing to a so far unallocated
1600 * block if the data is in fact not changed. */
1601 rc = vdWriteHelperOptimized(pDisk, pImage, pImageParentOverride,
1602 uOffsetCur, cbWriteCur,
1603 cbThisWrite, cbPreRead, cbPostRead,
1604 pcvBufCur, pvTmp);
1605 }
1606 else
1607 {
1608 /* Normal write, not optimized in any way. The block will
1609 * be written no matter what. This will usually (unless the
1610 * backend has some further optimization enabled) cause the
1611 * block to be allocated. */
1612 rc = vdWriteHelperStandard(pDisk, pImage, pImageParentOverride,
1613 uOffsetCur, cbWriteCur,
1614 cbThisWrite, cbPreRead, cbPostRead,
1615 pcvBufCur, pvTmp);
1616 }
1617 RTMemTmpFree(pvTmp);
1618 if (RT_FAILURE(rc))
1619 break;
1620 }
1621
1622 cbWriteCur -= cbThisWrite;
1623 uOffsetCur += cbThisWrite;
1624 pcvBufCur = (char *)pcvBufCur + cbThisWrite;
1625 } while (cbWriteCur != 0 && RT_SUCCESS(rc));
1626
1627 /* Update the cache on success */
1628 if ( RT_SUCCESS(rc)
1629 && pDisk->pCache
1630 && fUpdateCache)
1631 rc = vdCacheWriteHelper(pDisk->pCache, uOffset, pvBuf, cbWrite, NULL);
1632
1633 return rc;
1634}
1635
1636/**
1637 * internal: write a complete block (only used for diff images), taking the
1638 * remaining data from parent images. This implementation does not optimize
1639 * anything (except that it tries to read only that portions from parent
1640 * images that are really needed) - async version.
1641 */
1642static int vdWriteHelperStandardAsync(PVDIOCTX pIoCtx)
1643{
1644 int rc = VINF_SUCCESS;
1645
1646#if 0
1647
1648 /* Read the data that goes before the write to fill the block. */
1649 if (cbPreRead)
1650 {
1651 rc = vdReadHelperAsync(pIoCtxDst);
1652 if (RT_FAILURE(rc))
1653 return rc;
1654 }
1655
1656 /* Copy the data to the right place in the buffer. */
1657 vdIoCtxCopy(pIoCtxDst, pIoCtxSrc, cbThisWrite);
1658
1659 /* Read the data that goes after the write to fill the block. */
1660 if (cbPostRead)
1661 {
1662 /* If we have data to be written, use that instead of reading
1663 * data from the image. */
1664 size_t cbWriteCopy;
1665 if (cbWrite > cbThisWrite)
1666 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
1667 else
1668 cbWriteCopy = 0;
1669 /* Figure out how much we cannot read from the image, because
1670 * the last block to write might exceed the nominal size of the
1671 * image for technical reasons. */
1672 size_t cbFill;
1673 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
1674 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
1675 else
1676 cbFill = 0;
1677 /* The rest must be read from the image. */
1678 size_t cbReadImage = cbPostRead - cbWriteCopy - cbFill;
1679
1680 /* Now assemble the remaining data. */
1681 if (cbWriteCopy)
1682 {
1683 vdIoCtxCopy(pIoCtxDst, pIoCtxSrc, cbWriteCopy);
1684 ASMAtomicSubU32(&pIoCtxDst->cbTransferLeft, cbWriteCopy);
1685 }
1686
1687 if (cbReadImage)
1688 rc = vdReadHelperAsync(pDisk, pImage, pImageParentOverride, pIoCtxDst,
1689 uOffset + cbThisWrite + cbWriteCopy,
1690 cbReadImage);
1691 if (RT_FAILURE(rc))
1692 return rc;
1693 /* Zero out the remainder of this block. Will never be visible, as this
1694 * is beyond the limit of the image. */
1695 if (cbFill)
1696 {
1697 vdIoCtxSet(pIoCtxDst, '\0', cbFill);
1698 ASMAtomicSubU32(&pIoCtxDst->cbTransferLeft, cbFill);
1699 }
1700 }
1701
1702 if ( !pIoCtxDst->cbTransferLeft
1703 && !pIoCtxDst->cMetaTransfersPending
1704 && ASMAtomicCmpXchgBool(&pIoCtxDst->fComplete, true, false))
1705 {
1706 /* Write the full block to the virtual disk. */
1707 vdIoCtxChildReset(pIoCtxDst);
1708 rc = pImage->Backend->pfnAsyncWrite(pImage->pBackendData,
1709 uOffset - cbPreRead,
1710 cbPreRead + cbThisWrite + cbPostRead,
1711 pIoCtxDst,
1712 NULL, &cbPreRead, &cbPostRead, 0);
1713 Assert(rc != VERR_VD_BLOCK_FREE);
1714 Assert(cbPreRead == 0);
1715 Assert(cbPostRead == 0);
1716 }
1717 else
1718 {
1719 LogFlow(("cbTransferLeft=%u cMetaTransfersPending=%u fComplete=%RTbool\n",
1720 pIoCtxDst->cbTransferLeft, pIoCtxDst->cMetaTransfersPending,
1721 pIoCtxDst->fComplete));
1722 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1723 }
1724
1725 return rc;
1726#endif
1727 return VERR_NOT_IMPLEMENTED;
1728}
1729
1730static int vdWriteHelperOptimizedCmpAndWriteAsync(PVDIOCTX pIoCtx)
1731{
1732 int rc = VINF_SUCCESS;
1733 PVDIMAGE pImage = pIoCtx->pImage;
1734 size_t cbThisWrite = 0;
1735 size_t cbPreRead = pIoCtx->Type.Child.cbPreRead;
1736 size_t cbPostRead = pIoCtx->Type.Child.cbPostRead;
1737 size_t cbWriteCopy = pIoCtx->Type.Child.Write.Optimized.cbWriteCopy;
1738 size_t cbFill = pIoCtx->Type.Child.Write.Optimized.cbFill;
1739 size_t cbReadImage = pIoCtx->Type.Child.Write.Optimized.cbReadImage;
1740 PVDIOCTX pIoCtxParent = pIoCtx->pIoCtxParent;
1741
1742 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
1743
1744 AssertPtr(pIoCtxParent);
1745 Assert(!pIoCtxParent->pIoCtxParent);
1746 Assert(!pIoCtx->cbTransferLeft && !pIoCtx->cMetaTransfersPending);
1747
1748 vdIoCtxChildReset(pIoCtx);
1749 cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
1750 RTSgBufAdvance(&pIoCtx->SgBuf, cbPreRead);
1751
1752 /* Check if the write would modify anything in this block. */
1753 if (!RTSgBufCmp(&pIoCtx->SgBuf, &pIoCtxParent->SgBuf, cbThisWrite))
1754 {
1755 RTSGBUF SgBufSrcTmp;
1756
1757 RTSgBufClone(&SgBufSrcTmp, &pIoCtxParent->SgBuf);
1758 RTSgBufAdvance(&SgBufSrcTmp, cbThisWrite);
1759 RTSgBufAdvance(&pIoCtx->SgBuf, cbThisWrite);
1760
1761 if (!cbWriteCopy || !RTSgBufCmp(&pIoCtx->SgBuf, &SgBufSrcTmp, cbWriteCopy))
1762 {
1763 /* Block is completely unchanged, so no need to write anything. */
1764 LogFlowFunc(("Block didn't changed\n"));
1765 ASMAtomicWriteU32(&pIoCtx->cbTransferLeft, 0);
1766 RTSgBufAdvance(&pIoCtxParent->SgBuf, cbThisWrite);
1767 return VINF_VD_ASYNC_IO_FINISHED;
1768 }
1769 }
1770
1771 /* Copy the data to the right place in the buffer. */
1772 RTSgBufReset(&pIoCtx->SgBuf);
1773 RTSgBufAdvance(&pIoCtx->SgBuf, cbPreRead);
1774 vdIoCtxCopy(pIoCtx, pIoCtxParent, cbThisWrite);
1775
1776 /* Handle the data that goes after the write to fill the block. */
1777 if (cbPostRead)
1778 {
1779 /* Now assemble the remaining data. */
1780 if (cbWriteCopy)
1781 {
1782 /*
1783 * The S/G buffer of the parent needs to be cloned because
1784 * it is not allowed to modify the state.
1785 */
1786 RTSGBUF SgBufParentTmp;
1787
1788 RTSgBufClone(&SgBufParentTmp, &pIoCtxParent->SgBuf);
1789 RTSgBufCopy(&pIoCtx->SgBuf, &SgBufParentTmp, cbWriteCopy);
1790 }
1791
1792 /* Zero out the remainder of this block. Will never be visible, as this
1793 * is beyond the limit of the image. */
1794 if (cbFill)
1795 {
1796 RTSgBufAdvance(&pIoCtx->SgBuf, cbReadImage);
1797 vdIoCtxSet(pIoCtx, '\0', cbFill);
1798 }
1799 }
1800
1801 /* Write the full block to the virtual disk. */
1802 RTSgBufReset(&pIoCtx->SgBuf);
1803 rc = pImage->Backend->pfnAsyncWrite(pImage->pBackendData,
1804 pIoCtx->uOffset - cbPreRead,
1805 cbPreRead + cbThisWrite + cbPostRead,
1806 pIoCtx, NULL, &cbPreRead, &cbPostRead, 0);
1807 Assert(rc != VERR_VD_BLOCK_FREE);
1808 Assert(cbPreRead == 0);
1809 Assert(cbPostRead == 0);
1810 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1811 rc = VINF_SUCCESS;
1812 else if (rc == VERR_VD_IOCTX_HALT)
1813 {
1814 pIoCtx->fBlocked = true;
1815 rc = VINF_SUCCESS;
1816 }
1817
1818 return rc;
1819}
1820
1821static int vdWriteHelperOptimizedPreReadAsync(PVDIOCTX pIoCtx)
1822{
1823 int rc = VINF_SUCCESS;
1824
1825 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
1826
1827 if (pIoCtx->cbTransferLeft)
1828 rc = vdReadHelperAsync(pIoCtx);
1829
1830 if ( RT_SUCCESS(rc)
1831 && ( pIoCtx->cbTransferLeft
1832 || pIoCtx->cMetaTransfersPending))
1833 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1834 else
1835 pIoCtx->pfnIoCtxTransferNext = vdWriteHelperOptimizedCmpAndWriteAsync;
1836
1837 return rc;
1838}
1839
1840/**
1841 * internal: write a complete block (only used for diff images), taking the
1842 * remaining data from parent images. This implementation optimizes out writes
1843 * that do not change the data relative to the state as of the parent images.
1844 * All backends which support differential/growing images support this - async version.
1845 */
1846static int vdWriteHelperOptimizedAsync(PVDIOCTX pIoCtx)
1847{
1848 PVBOXHDD pDisk = pIoCtx->pDisk;
1849 uint64_t uOffset = pIoCtx->Type.Child.uOffsetSaved;
1850 size_t cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
1851 size_t cbPreRead = pIoCtx->Type.Child.cbPreRead;
1852 size_t cbPostRead = pIoCtx->Type.Child.cbPostRead;
1853 size_t cbWrite = pIoCtx->Type.Child.cbWriteParent;
1854 size_t cbFill = 0;
1855 size_t cbWriteCopy = 0;
1856 size_t cbReadImage = 0;
1857
1858 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
1859
1860 AssertPtr(pIoCtx->pIoCtxParent);
1861 Assert(!pIoCtx->pIoCtxParent->pIoCtxParent);
1862
1863 if (cbPostRead)
1864 {
1865 /* Figure out how much we cannot read from the image, because
1866 * the last block to write might exceed the nominal size of the
1867 * image for technical reasons. */
1868 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
1869 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
1870
1871 /* If we have data to be written, use that instead of reading
1872 * data from the image. */
1873 if (cbWrite > cbThisWrite)
1874 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
1875
1876 /* The rest must be read from the image. */
1877 cbReadImage = cbPostRead - cbWriteCopy - cbFill;
1878 }
1879
1880 pIoCtx->Type.Child.Write.Optimized.cbFill = cbFill;
1881 pIoCtx->Type.Child.Write.Optimized.cbWriteCopy = cbWriteCopy;
1882 pIoCtx->Type.Child.Write.Optimized.cbReadImage = cbReadImage;
1883
1884 /* Read the entire data of the block so that we can compare whether it will
1885 * be modified by the write or not. */
1886 pIoCtx->cbTransferLeft = cbPreRead + cbThisWrite + cbPostRead - cbFill;
1887 pIoCtx->cbTransfer = pIoCtx->cbTransferLeft;
1888 pIoCtx->uOffset -= cbPreRead;
1889
1890 /* Next step */
1891 pIoCtx->pfnIoCtxTransferNext = vdWriteHelperOptimizedPreReadAsync;
1892 return VINF_SUCCESS;
1893}
1894
1895/**
1896 * internal: write buffer to the image, taking care of block boundaries and
1897 * write optimizations - async version.
1898 */
1899static int vdWriteHelperAsync(PVDIOCTX pIoCtx)
1900{
1901 int rc;
1902 size_t cbWrite = pIoCtx->cbTransfer;
1903 uint64_t uOffset = pIoCtx->uOffset;
1904 PVDIMAGE pImage = pIoCtx->pImage;
1905 PVBOXHDD pDisk = pIoCtx->pDisk;
1906 unsigned fWrite;
1907 size_t cbThisWrite;
1908 size_t cbPreRead, cbPostRead;
1909
1910 /* Loop until all written. */
1911 do
1912 {
1913 /* Try to write the possibly partial block to the last opened image.
1914 * This works when the block is already allocated in this image or
1915 * if it is a full-block write (and allocation isn't suppressed below).
1916 * For image formats which don't support zero blocks, it's beneficial
1917 * to avoid unnecessarily allocating unchanged blocks. This prevents
1918 * unwanted expanding of images. VMDK is an example. */
1919 cbThisWrite = cbWrite;
1920 fWrite = (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
1921 ? 0 : VD_WRITE_NO_ALLOC;
1922 rc = pImage->Backend->pfnAsyncWrite(pImage->pBackendData, uOffset,
1923 cbThisWrite, pIoCtx,
1924 &cbThisWrite, &cbPreRead,
1925 &cbPostRead, fWrite);
1926 if (rc == VERR_VD_BLOCK_FREE)
1927 {
1928 /* Lock the disk .*/
1929 rc = vdIoCtxLockDisk(pDisk, pIoCtx);
1930 if (RT_SUCCESS(rc))
1931 {
1932 /*
1933 * Allocate segment and buffer in one go.
1934 * A bit hackish but avoids the need to allocate memory twice.
1935 */
1936 PRTSGSEG pTmp = (PRTSGSEG)RTMemAlloc(cbPreRead + cbThisWrite + cbPostRead + sizeof(RTSGSEG));
1937 AssertBreakStmt(VALID_PTR(pTmp), rc = VERR_NO_MEMORY);
1938
1939 pTmp->pvSeg = pTmp + 1;
1940 pTmp->cbSeg = cbPreRead + cbThisWrite + cbPostRead;
1941
1942 PVDIOCTX pIoCtxWrite = vdIoCtxChildAlloc(pDisk, VDIOCTXTXDIR_WRITE,
1943 uOffset, pTmp->cbSeg,
1944 pTmp, 1,
1945 pIoCtx, cbThisWrite,
1946 cbWrite,
1947 pTmp,
1948 (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
1949 ? vdWriteHelperStandardAsync
1950 : vdWriteHelperOptimizedAsync);
1951 if (!VALID_PTR(pIoCtxWrite))
1952 {
1953 RTMemTmpFree(pTmp);
1954 rc = VERR_NO_MEMORY;
1955 break;
1956 }
1957
1958 LogFlowFunc(("Disk is growing because of pIoCtx=%#p pIoCtxWrite=%#p\n",
1959 pIoCtx, pIoCtxWrite));
1960
1961 pIoCtxWrite->pImage = pImage;
1962 pIoCtxWrite->Type.Child.cbPreRead = cbPreRead;
1963 pIoCtxWrite->Type.Child.cbPostRead = cbPostRead;
1964
1965 /* Process the write request */
1966 rc = vdIoCtxProcess(pIoCtxWrite);
1967
1968 if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
1969 {
1970 vdIoCtxFree(pDisk, pIoCtxWrite);
1971 break;
1972 }
1973 else if ( rc == VINF_VD_ASYNC_IO_FINISHED
1974 && ASMAtomicCmpXchgBool(&pIoCtxWrite->fComplete, true, false))
1975 {
1976 LogFlow(("Child write request completed\n"));
1977 Assert(pIoCtx->cbTransferLeft >= cbThisWrite);
1978 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbThisWrite);
1979 ASMAtomicWriteBool(&pDisk->fLocked, false);
1980 vdIoCtxFree(pDisk, pIoCtxWrite);
1981
1982 rc = VINF_SUCCESS;
1983 }
1984 else
1985 {
1986 LogFlow(("Child write pending\n"));
1987 pIoCtx->fBlocked = true;
1988 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1989 cbWrite -= cbThisWrite;
1990 uOffset += cbThisWrite;
1991 break;
1992 }
1993 }
1994 else
1995 {
1996 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1997 break;
1998 }
1999 }
2000
2001 if (rc == VERR_VD_IOCTX_HALT)
2002 {
2003 cbWrite -= cbThisWrite;
2004 uOffset += cbThisWrite;
2005 pIoCtx->fBlocked = true;
2006 break;
2007 }
2008 else if (rc == VERR_VD_NOT_ENOUGH_METADATA)
2009 break;
2010
2011 cbWrite -= cbThisWrite;
2012 uOffset += cbThisWrite;
2013 } while (cbWrite != 0 && (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS));
2014
2015 if ( rc == VERR_VD_ASYNC_IO_IN_PROGRESS
2016 || rc == VERR_VD_NOT_ENOUGH_METADATA
2017 || rc == VERR_VD_IOCTX_HALT)
2018 {
2019 /*
2020 * Tell the caller that we don't need to go back here because all
2021 * writes are initiated.
2022 */
2023 if (!cbWrite)
2024 rc = VINF_SUCCESS;
2025
2026 pIoCtx->uOffset = uOffset;
2027 pIoCtx->cbTransfer = cbWrite;
2028 }
2029
2030 return rc;
2031}
2032
2033/**
2034 * Flush helper async version.
2035 */
2036static int vdFlushHelperAsync(PVDIOCTX pIoCtx)
2037{
2038 int rc = VINF_SUCCESS;
2039 PVBOXHDD pDisk = pIoCtx->pDisk;
2040 PVDIMAGE pImage = pIoCtx->pImage;
2041
2042 rc = vdIoCtxLockDisk(pDisk, pIoCtx);
2043 if (RT_SUCCESS(rc))
2044 {
2045 vdResetModifiedFlag(pDisk);
2046 rc = pImage->Backend->pfnAsyncFlush(pImage->pBackendData, pIoCtx);
2047 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2048 rc = VINF_SUCCESS;
2049 }
2050
2051 return rc;
2052}
2053
2054/**
2055 * internal: scans plugin directory and loads the backends have been found.
2056 */
2057static int vdLoadDynamicBackends()
2058{
2059#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
2060 int rc = VINF_SUCCESS;
2061 PRTDIR pPluginDir = NULL;
2062
2063 /* Enumerate plugin backends. */
2064 char szPath[RTPATH_MAX];
2065 rc = RTPathAppPrivateArch(szPath, sizeof(szPath));
2066 if (RT_FAILURE(rc))
2067 return rc;
2068
2069 /* To get all entries with VBoxHDD as prefix. */
2070 char *pszPluginFilter = RTPathJoinA(szPath, VBOX_HDDFORMAT_PLUGIN_PREFIX "*");
2071 if (!pszPluginFilter)
2072 return VERR_NO_STR_MEMORY;
2073
2074 PRTDIRENTRYEX pPluginDirEntry = NULL;
2075 size_t cbPluginDirEntry = sizeof(RTDIRENTRYEX);
2076 /* The plugins are in the same directory as the other shared libs. */
2077 rc = RTDirOpenFiltered(&pPluginDir, pszPluginFilter, RTDIRFILTER_WINNT);
2078 if (RT_FAILURE(rc))
2079 {
2080 /* On Windows the above immediately signals that there are no
2081 * files matching, while on other platforms enumerating the
2082 * files below fails. Either way: no plugins. */
2083 goto out;
2084 }
2085
2086 pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
2087 if (!pPluginDirEntry)
2088 {
2089 rc = VERR_NO_MEMORY;
2090 goto out;
2091 }
2092
2093 while ((rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK)) != VERR_NO_MORE_FILES)
2094 {
2095 RTLDRMOD hPlugin = NIL_RTLDRMOD;
2096 PFNVBOXHDDFORMATLOAD pfnHDDFormatLoad = NULL;
2097 PVBOXHDDBACKEND pBackend = NULL;
2098 char *pszPluginPath = NULL;
2099
2100 if (rc == VERR_BUFFER_OVERFLOW)
2101 {
2102 /* allocate new buffer. */
2103 RTMemFree(pPluginDirEntry);
2104 pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbPluginDirEntry);
2105 if (!pPluginDirEntry)
2106 {
2107 rc = VERR_NO_MEMORY;
2108 break;
2109 }
2110 /* Retry. */
2111 rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
2112 if (RT_FAILURE(rc))
2113 break;
2114 }
2115 else if (RT_FAILURE(rc))
2116 break;
2117
2118 /* We got the new entry. */
2119 if (!RTFS_IS_FILE(pPluginDirEntry->Info.Attr.fMode))
2120 continue;
2121
2122 /* Prepend the path to the libraries. */
2123 pszPluginPath = RTPathJoinA(szPath, pPluginDirEntry->szName);
2124 if (!pszPluginPath)
2125 {
2126 rc = VERR_NO_STR_MEMORY;
2127 break;
2128 }
2129
2130 rc = SUPR3HardenedLdrLoad(pszPluginPath, &hPlugin);
2131 if (RT_SUCCESS(rc))
2132 {
2133 rc = RTLdrGetSymbol(hPlugin, VBOX_HDDFORMAT_LOAD_NAME, (void**)&pfnHDDFormatLoad);
2134 if (RT_FAILURE(rc) || !pfnHDDFormatLoad)
2135 {
2136 LogFunc(("error resolving the entry point %s in plugin %s, rc=%Rrc, pfnHDDFormat=%#p\n", VBOX_HDDFORMAT_LOAD_NAME, pPluginDirEntry->szName, rc, pfnHDDFormatLoad));
2137 if (RT_SUCCESS(rc))
2138 rc = VERR_SYMBOL_NOT_FOUND;
2139 }
2140
2141 if (RT_SUCCESS(rc))
2142 {
2143 /* Get the function table. */
2144 rc = pfnHDDFormatLoad(&pBackend);
2145 if (RT_SUCCESS(rc) && pBackend->cbSize == sizeof(VBOXHDDBACKEND))
2146 {
2147 pBackend->hPlugin = hPlugin;
2148 vdAddBackend(pBackend);
2149 }
2150 else
2151 LogFunc(("ignored plugin '%s': pBackend->cbSize=%d rc=%Rrc\n", pszPluginPath, pBackend->cbSize, rc));
2152 }
2153 else
2154 LogFunc(("ignored plugin '%s': rc=%Rrc\n", pszPluginPath, rc));
2155
2156 if (RT_FAILURE(rc))
2157 RTLdrClose(hPlugin);
2158 }
2159 RTStrFree(pszPluginPath);
2160 }
2161out:
2162 if (rc == VERR_NO_MORE_FILES)
2163 rc = VINF_SUCCESS;
2164 RTStrFree(pszPluginFilter);
2165 if (pPluginDirEntry)
2166 RTMemFree(pPluginDirEntry);
2167 if (pPluginDir)
2168 RTDirClose(pPluginDir);
2169 return rc;
2170#else
2171 return VINF_SUCCESS;
2172#endif
2173}
2174
2175/**
2176 * internal: scans plugin directory and loads the cache backends have been found.
2177 */
2178static int vdLoadDynamicCacheBackends()
2179{
2180#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
2181 int rc = VINF_SUCCESS;
2182 PRTDIR pPluginDir = NULL;
2183
2184 /* Enumerate plugin backends. */
2185 char szPath[RTPATH_MAX];
2186 rc = RTPathAppPrivateArch(szPath, sizeof(szPath));
2187 if (RT_FAILURE(rc))
2188 return rc;
2189
2190 /* To get all entries with VBoxHDD as prefix. */
2191 char *pszPluginFilter = RTPathJoinA(szPath, VD_CACHEFORMAT_PLUGIN_PREFIX "*");
2192 if (!pszPluginFilter)
2193 {
2194 rc = VERR_NO_STR_MEMORY;
2195 return rc;
2196 }
2197
2198 PRTDIRENTRYEX pPluginDirEntry = NULL;
2199 size_t cbPluginDirEntry = sizeof(RTDIRENTRYEX);
2200 /* The plugins are in the same directory as the other shared libs. */
2201 rc = RTDirOpenFiltered(&pPluginDir, pszPluginFilter, RTDIRFILTER_WINNT);
2202 if (RT_FAILURE(rc))
2203 {
2204 /* On Windows the above immediately signals that there are no
2205 * files matching, while on other platforms enumerating the
2206 * files below fails. Either way: no plugins. */
2207 goto out;
2208 }
2209
2210 pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
2211 if (!pPluginDirEntry)
2212 {
2213 rc = VERR_NO_MEMORY;
2214 goto out;
2215 }
2216
2217 while ((rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK)) != VERR_NO_MORE_FILES)
2218 {
2219 RTLDRMOD hPlugin = NIL_RTLDRMOD;
2220 PFNVDCACHEFORMATLOAD pfnVDCacheLoad = NULL;
2221 PVDCACHEBACKEND pBackend = NULL;
2222 char *pszPluginPath = NULL;
2223
2224 if (rc == VERR_BUFFER_OVERFLOW)
2225 {
2226 /* allocate new buffer. */
2227 RTMemFree(pPluginDirEntry);
2228 pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbPluginDirEntry);
2229 if (!pPluginDirEntry)
2230 {
2231 rc = VERR_NO_MEMORY;
2232 break;
2233 }
2234 /* Retry. */
2235 rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
2236 if (RT_FAILURE(rc))
2237 break;
2238 }
2239 else if (RT_FAILURE(rc))
2240 break;
2241
2242 /* We got the new entry. */
2243 if (!RTFS_IS_FILE(pPluginDirEntry->Info.Attr.fMode))
2244 continue;
2245
2246 /* Prepend the path to the libraries. */
2247 pszPluginPath = RTPathJoinA(szPath, pPluginDirEntry->szName);
2248 if (!pszPluginPath)
2249 {
2250 rc = VERR_NO_STR_MEMORY;
2251 break;
2252 }
2253
2254 rc = SUPR3HardenedLdrLoad(pszPluginPath, &hPlugin);
2255 if (RT_SUCCESS(rc))
2256 {
2257 rc = RTLdrGetSymbol(hPlugin, VD_CACHEFORMAT_LOAD_NAME, (void**)&pfnVDCacheLoad);
2258 if (RT_FAILURE(rc) || !pfnVDCacheLoad)
2259 {
2260 LogFunc(("error resolving the entry point %s in plugin %s, rc=%Rrc, pfnVDCacheLoad=%#p\n",
2261 VD_CACHEFORMAT_LOAD_NAME, pPluginDirEntry->szName, rc, pfnVDCacheLoad));
2262 if (RT_SUCCESS(rc))
2263 rc = VERR_SYMBOL_NOT_FOUND;
2264 }
2265
2266 if (RT_SUCCESS(rc))
2267 {
2268 /* Get the function table. */
2269 rc = pfnVDCacheLoad(&pBackend);
2270 if (RT_SUCCESS(rc) && pBackend->cbSize == sizeof(VDCACHEBACKEND))
2271 {
2272 pBackend->hPlugin = hPlugin;
2273 vdAddCacheBackend(pBackend);
2274 }
2275 else
2276 LogFunc(("ignored plugin '%s': pBackend->cbSize=%d rc=%Rrc\n", pszPluginPath, pBackend->cbSize, rc));
2277 }
2278 else
2279 LogFunc(("ignored plugin '%s': rc=%Rrc\n", pszPluginPath, rc));
2280
2281 if (RT_FAILURE(rc))
2282 RTLdrClose(hPlugin);
2283 }
2284 RTStrFree(pszPluginPath);
2285 }
2286out:
2287 if (rc == VERR_NO_MORE_FILES)
2288 rc = VINF_SUCCESS;
2289 RTStrFree(pszPluginFilter);
2290 if (pPluginDirEntry)
2291 RTMemFree(pPluginDirEntry);
2292 if (pPluginDir)
2293 RTDirClose(pPluginDir);
2294 return rc;
2295#else
2296 return VINF_SUCCESS;
2297#endif
2298}
2299
2300/**
2301 * VD async I/O interface open callback.
2302 */
2303static int vdIOOpenFallback(void *pvUser, const char *pszLocation,
2304 uint32_t fOpen, PFNVDCOMPLETED pfnCompleted,
2305 void **ppStorage)
2306{
2307 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)RTMemAllocZ(sizeof(VDIIOFALLBACKSTORAGE));
2308
2309 if (!pStorage)
2310 return VERR_NO_MEMORY;
2311
2312 pStorage->pfnCompleted = pfnCompleted;
2313
2314 /* Open the file. */
2315 int rc = RTFileOpen(&pStorage->File, pszLocation, fOpen);
2316 if (RT_SUCCESS(rc))
2317 {
2318 *ppStorage = pStorage;
2319 return VINF_SUCCESS;
2320 }
2321
2322 RTMemFree(pStorage);
2323 return rc;
2324}
2325
2326/**
2327 * VD async I/O interface close callback.
2328 */
2329static int vdIOCloseFallback(void *pvUser, void *pvStorage)
2330{
2331 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
2332
2333 RTFileClose(pStorage->File);
2334 RTMemFree(pStorage);
2335 return VINF_SUCCESS;
2336}
2337
2338static int vdIODeleteFallback(void *pvUser, const char *pcszFilename)
2339{
2340 return RTFileDelete(pcszFilename);
2341}
2342
2343static int vdIOMoveFallback(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove)
2344{
2345 return RTFileMove(pcszSrc, pcszDst, fMove);
2346}
2347
2348static int vdIOGetFreeSpaceFallback(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace)
2349{
2350 return RTFsQuerySizes(pcszFilename, NULL, pcbFreeSpace, NULL, NULL);
2351}
2352
2353static int vdIOGetModificationTimeFallback(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime)
2354{
2355 RTFSOBJINFO info;
2356 int rc = RTPathQueryInfo(pcszFilename, &info, RTFSOBJATTRADD_NOTHING);
2357 if (RT_SUCCESS(rc))
2358 *pModificationTime = info.ModificationTime;
2359 return rc;
2360}
2361
2362/**
2363 * VD async I/O interface callback for retrieving the file size.
2364 */
2365static int vdIOGetSizeFallback(void *pvUser, void *pvStorage, uint64_t *pcbSize)
2366{
2367 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
2368
2369 return RTFileGetSize(pStorage->File, pcbSize);
2370}
2371
2372/**
2373 * VD async I/O interface callback for setting the file size.
2374 */
2375static int vdIOSetSizeFallback(void *pvUser, void *pvStorage, uint64_t cbSize)
2376{
2377 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
2378
2379 return RTFileSetSize(pStorage->File, cbSize);
2380}
2381
2382/**
2383 * VD async I/O interface callback for a synchronous write to the file.
2384 */
2385static int vdIOWriteSyncFallback(void *pvUser, void *pvStorage, uint64_t uOffset,
2386 const void *pvBuf, size_t cbWrite, size_t *pcbWritten)
2387{
2388 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
2389
2390 return RTFileWriteAt(pStorage->File, uOffset, pvBuf, cbWrite, pcbWritten);
2391}
2392
2393/**
2394 * VD async I/O interface callback for a synchronous read from the file.
2395 */
2396static int vdIOReadSyncFallback(void *pvUser, void *pvStorage, uint64_t uOffset,
2397 void *pvBuf, size_t cbRead, size_t *pcbRead)
2398{
2399 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
2400
2401 return RTFileReadAt(pStorage->File, uOffset, pvBuf, cbRead, pcbRead);
2402}
2403
2404/**
2405 * VD async I/O interface callback for a synchronous flush of the file data.
2406 */
2407static int vdIOFlushSyncFallback(void *pvUser, void *pvStorage)
2408{
2409 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
2410
2411 return RTFileFlush(pStorage->File);
2412}
2413
2414/**
2415 * VD async I/O interface callback for a asynchronous read from the file.
2416 */
2417static int vdIOReadAsyncFallback(void *pvUser, void *pStorage, uint64_t uOffset,
2418 PCRTSGSEG paSegments, size_t cSegments,
2419 size_t cbRead, void *pvCompletion,
2420 void **ppTask)
2421{
2422 return VERR_NOT_IMPLEMENTED;
2423}
2424
2425/**
2426 * VD async I/O interface callback for a asynchronous write to the file.
2427 */
2428static int vdIOWriteAsyncFallback(void *pvUser, void *pStorage, uint64_t uOffset,
2429 PCRTSGSEG paSegments, size_t cSegments,
2430 size_t cbWrite, void *pvCompletion,
2431 void **ppTask)
2432{
2433 return VERR_NOT_IMPLEMENTED;
2434}
2435
2436/**
2437 * VD async I/O interface callback for a asynchronous flush of the file data.
2438 */
2439static int vdIOFlushAsyncFallback(void *pvUser, void *pStorage,
2440 void *pvCompletion, void **ppTask)
2441{
2442 return VERR_NOT_IMPLEMENTED;
2443}
2444
2445/**
2446 * Internal - Continues an I/O context after
2447 * it was halted because of an active transfer.
2448 */
2449static int vdIoCtxContinue(PVDIOCTX pIoCtx, int rcReq)
2450{
2451 PVBOXHDD pDisk = pIoCtx->pDisk;
2452 int rc = VINF_SUCCESS;
2453
2454 if (RT_FAILURE(rcReq))
2455 ASMAtomicCmpXchgS32(&pIoCtx->rcReq, rcReq, VINF_SUCCESS);
2456
2457 if (!pIoCtx->fBlocked)
2458 {
2459 /* Continue the transfer */
2460 rc = vdIoCtxProcess(pIoCtx);
2461
2462 if ( rc == VINF_VD_ASYNC_IO_FINISHED
2463 && ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
2464 {
2465 LogFlowFunc(("I/O context completed pIoCtx=%#p\n", pIoCtx));
2466 if (pIoCtx->pIoCtxParent)
2467 {
2468 PVDIOCTX pIoCtxParent = pIoCtx->pIoCtxParent;
2469
2470 LogFlowFunc(("I/O context transferred %u bytes for the parent pIoCtxParent=%p\n",
2471 pIoCtx->Type.Child.cbTransferParent, pIoCtxParent));
2472
2473 /* Update the parent state. */
2474 Assert(!pIoCtxParent->pIoCtxParent);
2475 Assert(pIoCtx->enmTxDir == VDIOCTXTXDIR_WRITE);
2476 Assert(pIoCtxParent->cbTransferLeft >= pIoCtx->Type.Child.cbTransferParent);
2477 ASMAtomicSubU32(&pIoCtxParent->cbTransferLeft, pIoCtx->Type.Child.cbTransferParent);
2478
2479 if (RT_FAILURE(pIoCtx->rcReq))
2480 ASMAtomicCmpXchgS32(&pIoCtxParent->rcReq, pIoCtx->rcReq, VINF_SUCCESS);
2481
2482 /*
2483 * A completed child write means that we finished growing the image.
2484 * We have to process any pending writes now.
2485 */
2486 Assert(pDisk->fLocked);
2487 ASMAtomicWriteBool(&pDisk->fLocked, false);
2488
2489 /* Unblock the parent */
2490 pIoCtxParent->fBlocked = false;
2491
2492 rc = vdIoCtxProcess(pIoCtxParent);
2493
2494 if ( rc == VINF_VD_ASYNC_IO_FINISHED
2495 && ASMAtomicCmpXchgBool(&pIoCtxParent->fComplete, true, false))
2496 {
2497 LogFlowFunc(("Parent I/O context completed pIoCtxParent=%#p rcReq=%Rrc\n", pIoCtxParent, pIoCtxParent->rcReq));
2498 pIoCtxParent->Type.Root.pfnComplete(pIoCtxParent->Type.Root.pvUser1,
2499 pIoCtxParent->Type.Root.pvUser2,
2500 pIoCtxParent->rcReq);
2501 vdThreadFinishWrite(pDisk);
2502 vdIoCtxFree(pDisk, pIoCtxParent);
2503 }
2504
2505 /* Process any pending writes if the current request didn't caused another growing. */
2506 RTCritSectEnter(&pDisk->CritSect);
2507
2508 if (!RTListIsEmpty(&pDisk->ListWriteLocked) && !pDisk->fLocked)
2509 {
2510 RTLISTNODE ListTmp;
2511
2512 LogFlowFunc(("Before: pNext=%#p pPrev=%#p\n", pDisk->ListWriteLocked.pNext,
2513 pDisk->ListWriteLocked.pPrev));
2514
2515 RTListMove(&ListTmp, &pDisk->ListWriteLocked);
2516
2517 LogFlowFunc(("After: pNext=%#p pPrev=%#p\n", pDisk->ListWriteLocked.pNext,
2518 pDisk->ListWriteLocked.pPrev));
2519
2520 RTCritSectLeave(&pDisk->CritSect);
2521
2522 /* Process the list. */
2523 do
2524 {
2525 PVDIOCTXDEFERRED pDeferred = RTListNodeGetFirst(&ListTmp, VDIOCTXDEFERRED, NodeDeferred);
2526 PVDIOCTX pIoCtxWait = pDeferred->pIoCtx;
2527
2528 AssertPtr(pIoCtxWait);
2529
2530 RTListNodeRemove(&pDeferred->NodeDeferred);
2531 RTMemFree(pDeferred);
2532
2533 Assert(!pIoCtxWait->pIoCtxParent);
2534
2535 pIoCtxWait->fBlocked = false;
2536 LogFlowFunc(("Processing waiting I/O context pIoCtxWait=%#p\n", pIoCtxWait));
2537
2538 rc = vdIoCtxProcess(pIoCtxWait);
2539 if ( rc == VINF_VD_ASYNC_IO_FINISHED
2540 && ASMAtomicCmpXchgBool(&pIoCtxWait->fComplete, true, false))
2541 {
2542 LogFlowFunc(("Waiting I/O context completed pIoCtxWait=%#p\n", pIoCtxWait));
2543 vdThreadFinishWrite(pDisk);
2544 pIoCtxWait->Type.Root.pfnComplete(pIoCtxWait->Type.Root.pvUser1,
2545 pIoCtxWait->Type.Root.pvUser2,
2546 pIoCtxWait->rcReq);
2547 vdIoCtxFree(pDisk, pIoCtxWait);
2548 }
2549 } while (!RTListIsEmpty(&ListTmp));
2550 }
2551 else
2552 RTCritSectLeave(&pDisk->CritSect);
2553 }
2554 else
2555 {
2556 if (pIoCtx->enmTxDir == VDIOCTXTXDIR_FLUSH)
2557 {
2558 vdIoCtxUnlockDisk(pDisk, pIoCtx);
2559 vdThreadFinishWrite(pDisk);
2560 }
2561 else if (pIoCtx->enmTxDir == VDIOCTXTXDIR_WRITE)
2562 vdThreadFinishWrite(pDisk);
2563 else
2564 {
2565 Assert(pIoCtx->enmTxDir == VDIOCTXTXDIR_READ);
2566 vdThreadFinishRead(pDisk);
2567 }
2568
2569 LogFlowFunc(("I/O context completed pIoCtx=%#p rcReq=%Rrc\n", pIoCtx, pIoCtx->rcReq));
2570 pIoCtx->Type.Root.pfnComplete(pIoCtx->Type.Root.pvUser1,
2571 pIoCtx->Type.Root.pvUser2,
2572 pIoCtx->rcReq);
2573 }
2574
2575 vdIoCtxFree(pDisk, pIoCtx);
2576 }
2577 }
2578
2579 return VINF_SUCCESS;
2580}
2581
2582/**
2583 * Internal - Called when user transfer completed.
2584 */
2585static int vdUserXferCompleted(PVDIOSTORAGE pIoStorage, PVDIOCTX pIoCtx,
2586 PFNVDXFERCOMPLETED pfnComplete, void *pvUser,
2587 size_t cbTransfer, int rcReq)
2588{
2589 int rc = VINF_SUCCESS;
2590 bool fIoCtxContinue = true;
2591 PVBOXHDD pDisk = pIoCtx->pDisk;
2592
2593 LogFlowFunc(("pIoStorage=%#p pIoCtx=%#p pfnComplete=%#p pvUser=%#p cbTransfer=%zu rcReq=%Rrc\n",
2594 pIoStorage, pIoCtx, pfnComplete, pvUser, cbTransfer, rcReq));
2595
2596 Assert(pIoCtx->cbTransferLeft >= cbTransfer);
2597 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbTransfer);
2598 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
2599
2600 if (pfnComplete)
2601 {
2602 RTCritSectEnter(&pDisk->CritSect);
2603 rc = pfnComplete(pIoStorage->pVDIo->pBackendData, pIoCtx, pvUser, rcReq);
2604 RTCritSectLeave(&pDisk->CritSect);
2605 }
2606
2607 if (RT_SUCCESS(rc))
2608 rc = vdIoCtxContinue(pIoCtx, rcReq);
2609 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2610 rc = VINF_SUCCESS;
2611
2612 return rc;
2613}
2614
2615/**
2616 * Internal - Called when a meta transfer completed.
2617 */
2618static int vdMetaXferCompleted(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnComplete, void *pvUser,
2619 PVDMETAXFER pMetaXfer, int rcReq)
2620{
2621 PVBOXHDD pDisk = pIoStorage->pVDIo->pDisk;
2622 RTLISTNODE ListIoCtxWaiting;
2623 bool fFlush;
2624
2625 LogFlowFunc(("pIoStorage=%#p pfnComplete=%#p pvUser=%#p pMetaXfer=%#p rcReq=%Rrc\n",
2626 pIoStorage, pfnComplete, pvUser, pMetaXfer, rcReq));
2627
2628 RTCritSectEnter(&pDisk->CritSect);
2629 fFlush = VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_FLUSH;
2630 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
2631
2632 if (!fFlush)
2633 {
2634 RTListMove(&ListIoCtxWaiting, &pMetaXfer->ListIoCtxWaiting);
2635
2636 if (RT_FAILURE(rcReq))
2637 {
2638 /* Remove from the AVL tree. */
2639 LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
2640 bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
2641 Assert(fRemoved);
2642 RTMemFree(pMetaXfer);
2643 }
2644 else
2645 {
2646 /* Increase the reference counter to make sure it doesn't go away before the last context is processed. */
2647 pMetaXfer->cRefs++;
2648 }
2649 }
2650 else
2651 RTListMove(&ListIoCtxWaiting, &pMetaXfer->ListIoCtxWaiting);
2652 RTCritSectLeave(&pDisk->CritSect);
2653
2654 /* Go through the waiting list and continue the I/O contexts. */
2655 while (!RTListIsEmpty(&ListIoCtxWaiting))
2656 {
2657 int rc = VINF_SUCCESS;
2658 bool fContinue = true;
2659 PVDIOCTXDEFERRED pDeferred = RTListNodeGetFirst(&ListIoCtxWaiting, VDIOCTXDEFERRED, NodeDeferred);
2660 PVDIOCTX pIoCtx = pDeferred->pIoCtx;
2661 RTListNodeRemove(&pDeferred->NodeDeferred);
2662
2663 RTMemFree(pDeferred);
2664 ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
2665
2666 if (pfnComplete)
2667 {
2668 RTCritSectEnter(&pDisk->CritSect);
2669 rc = pfnComplete(pIoStorage->pVDIo->pBackendData, pIoCtx, pvUser, rcReq);
2670 RTCritSectLeave(&pDisk->CritSect);
2671 }
2672
2673 LogFlow(("Completion callback for I/O context %#p returned %Rrc\n", pIoCtx, rc));
2674
2675 if (RT_SUCCESS(rc))
2676 {
2677 rc = vdIoCtxContinue(pIoCtx, rcReq);
2678 AssertRC(rc);
2679 }
2680 else
2681 Assert(rc == VERR_VD_ASYNC_IO_IN_PROGRESS);
2682 }
2683
2684 /* Remove if not used anymore. */
2685 if (RT_SUCCESS(rcReq) && !fFlush)
2686 {
2687 RTCritSectEnter(&pDisk->CritSect);
2688 pMetaXfer->cRefs--;
2689 if (!pMetaXfer->cRefs && RTListIsEmpty(&pMetaXfer->ListIoCtxWaiting))
2690 {
2691 /* Remove from the AVL tree. */
2692 LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
2693 bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
2694 Assert(fRemoved);
2695 RTMemFree(pMetaXfer);
2696 }
2697 RTCritSectLeave(&pDisk->CritSect);
2698 }
2699 else if (fFlush)
2700 RTMemFree(pMetaXfer);
2701
2702 return VINF_SUCCESS;
2703}
2704
2705static int vdIOIntReqCompleted(void *pvUser, int rcReq)
2706{
2707 int rc = VINF_SUCCESS;
2708 PVDIOTASK pIoTask = (PVDIOTASK)pvUser;
2709 PVDIOSTORAGE pIoStorage = pIoTask->pIoStorage;
2710
2711 LogFlowFunc(("Task completed pIoTask=%#p\n", pIoTask));
2712
2713 if (!pIoTask->fMeta)
2714 rc = vdUserXferCompleted(pIoStorage, pIoTask->Type.User.pIoCtx,
2715 pIoTask->pfnComplete, pIoTask->pvUser,
2716 pIoTask->Type.User.cbTransfer, rcReq);
2717 else
2718 rc = vdMetaXferCompleted(pIoStorage, pIoTask->pfnComplete, pIoTask->pvUser,
2719 pIoTask->Type.Meta.pMetaXfer, rcReq);
2720
2721 vdIoTaskFree(pIoStorage->pVDIo->pDisk, pIoTask);
2722
2723 return rc;
2724}
2725
2726/**
2727 * VD I/O interface callback for opening a file.
2728 */
2729static int vdIOIntOpen(void *pvUser, const char *pszLocation,
2730 unsigned uOpenFlags, PPVDIOSTORAGE ppIoStorage)
2731{
2732 int rc = VINF_SUCCESS;
2733 PVDIO pVDIo = (PVDIO)pvUser;
2734 PVDIOSTORAGE pIoStorage = (PVDIOSTORAGE)RTMemAllocZ(sizeof(VDIOSTORAGE));
2735
2736 if (!pIoStorage)
2737 return VERR_NO_MEMORY;
2738
2739 /* Create the AVl tree. */
2740 pIoStorage->pTreeMetaXfers = (PAVLRFOFFTREE)RTMemAllocZ(sizeof(AVLRFOFFTREE));
2741 if (pIoStorage->pTreeMetaXfers)
2742 {
2743 rc = pVDIo->pInterfaceIOCallbacks->pfnOpen(pVDIo->pInterfaceIO->pvUser,
2744 pszLocation, uOpenFlags,
2745 vdIOIntReqCompleted,
2746 &pIoStorage->pStorage);
2747 if (RT_SUCCESS(rc))
2748 {
2749 pIoStorage->pVDIo = pVDIo;
2750 *ppIoStorage = pIoStorage;
2751 return VINF_SUCCESS;
2752 }
2753
2754 RTMemFree(pIoStorage->pTreeMetaXfers);
2755 }
2756 else
2757 rc = VERR_NO_MEMORY;
2758
2759 RTMemFree(pIoStorage);
2760 return rc;
2761}
2762
2763static int vdIOIntTreeMetaXferDestroy(PAVLRFOFFNODECORE pNode, void *pvUser)
2764{
2765 AssertMsgFailed(("Tree should be empty at this point!\n"));
2766 return VINF_SUCCESS;
2767}
2768
2769static int vdIOIntClose(void *pvUser, PVDIOSTORAGE pIoStorage)
2770{
2771 PVDIO pVDIo = (PVDIO)pvUser;
2772
2773 int rc = pVDIo->pInterfaceIOCallbacks->pfnClose(pVDIo->pInterfaceIO->pvUser,
2774 pIoStorage->pStorage);
2775 AssertRC(rc);
2776
2777 RTAvlrFileOffsetDestroy(pIoStorage->pTreeMetaXfers, vdIOIntTreeMetaXferDestroy, NULL);
2778 RTMemFree(pIoStorage->pTreeMetaXfers);
2779 RTMemFree(pIoStorage);
2780 return VINF_SUCCESS;
2781}
2782
2783static int vdIOIntDelete(void *pvUser, const char *pcszFilename)
2784{
2785 PVDIO pVDIo = (PVDIO)pvUser;
2786 return pVDIo->pInterfaceIOCallbacks->pfnDelete(pVDIo->pInterfaceIO->pvUser,
2787 pcszFilename);
2788}
2789
2790static int vdIOIntMove(void *pvUser, const char *pcszSrc, const char *pcszDst,
2791 unsigned fMove)
2792{
2793 PVDIO pVDIo = (PVDIO)pvUser;
2794 return pVDIo->pInterfaceIOCallbacks->pfnMove(pVDIo->pInterfaceIO->pvUser,
2795 pcszSrc, pcszDst, fMove);
2796}
2797
2798static int vdIOIntGetFreeSpace(void *pvUser, const char *pcszFilename,
2799 int64_t *pcbFreeSpace)
2800{
2801 PVDIO pVDIo = (PVDIO)pvUser;
2802 return pVDIo->pInterfaceIOCallbacks->pfnGetFreeSpace(pVDIo->pInterfaceIO->pvUser,
2803 pcszFilename,
2804 pcbFreeSpace);
2805}
2806
2807static int vdIOIntGetModificationTime(void *pvUser, const char *pcszFilename,
2808 PRTTIMESPEC pModificationTime)
2809{
2810 PVDIO pVDIo = (PVDIO)pvUser;
2811 return pVDIo->pInterfaceIOCallbacks->pfnGetModificationTime(pVDIo->pInterfaceIO->pvUser,
2812 pcszFilename,
2813 pModificationTime);
2814}
2815
2816static int vdIOIntGetSize(void *pvUser, PVDIOSTORAGE pIoStorage,
2817 uint64_t *pcbSize)
2818{
2819 PVDIO pVDIo = (PVDIO)pvUser;
2820 return pVDIo->pInterfaceIOCallbacks->pfnGetSize(pVDIo->pInterfaceIO->pvUser,
2821 pIoStorage->pStorage,
2822 pcbSize);
2823}
2824
2825static int vdIOIntSetSize(void *pvUser, PVDIOSTORAGE pIoStorage,
2826 uint64_t cbSize)
2827{
2828 PVDIO pVDIo = (PVDIO)pvUser;
2829
2830 return pVDIo->pInterfaceIOCallbacks->pfnSetSize(pVDIo->pInterfaceIO->pvUser,
2831 pIoStorage->pStorage,
2832 cbSize);
2833}
2834
2835static int vdIOIntWriteSync(void *pvUser, PVDIOSTORAGE pIoStorage,
2836 uint64_t uOffset, const void *pvBuf,
2837 size_t cbWrite, size_t *pcbWritten)
2838{
2839 PVDIO pVDIo = (PVDIO)pvUser;
2840
2841 return pVDIo->pInterfaceIOCallbacks->pfnWriteSync(pVDIo->pInterfaceIO->pvUser,
2842 pIoStorage->pStorage,
2843 uOffset, pvBuf, cbWrite,
2844 pcbWritten);
2845}
2846
2847static int vdIOIntReadSync(void *pvUser, PVDIOSTORAGE pIoStorage,
2848 uint64_t uOffset, void *pvBuf, size_t cbRead,
2849 size_t *pcbRead)
2850{
2851 PVDIO pVDIo = (PVDIO)pvUser;
2852 return pVDIo->pInterfaceIOCallbacks->pfnReadSync(pVDIo->pInterfaceIO->pvUser,
2853 pIoStorage->pStorage,
2854 uOffset, pvBuf, cbRead,
2855 pcbRead);
2856}
2857
2858static int vdIOIntFlushSync(void *pvUser, PVDIOSTORAGE pIoStorage)
2859{
2860 PVDIO pVDIo = (PVDIO)pvUser;
2861 return pVDIo->pInterfaceIOCallbacks->pfnFlushSync(pVDIo->pInterfaceIO->pvUser,
2862 pIoStorage->pStorage);
2863}
2864
2865static int vdIOIntReadUserAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
2866 uint64_t uOffset, PVDIOCTX pIoCtx,
2867 size_t cbRead)
2868{
2869 int rc = VINF_SUCCESS;
2870 PVDIO pVDIo = (PVDIO)pvUser;
2871 PVBOXHDD pDisk = pVDIo->pDisk;
2872
2873 LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pIoCtx=%#p cbRead=%u\n",
2874 pvUser, pIoStorage, uOffset, pIoCtx, cbRead));
2875
2876 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
2877
2878 /* Build the S/G array and spawn a new I/O task */
2879 while (cbRead)
2880 {
2881 RTSGSEG aSeg[VD_IO_TASK_SEGMENTS_MAX];
2882 unsigned cSegments = VD_IO_TASK_SEGMENTS_MAX;
2883 size_t cbTaskRead = 0;
2884
2885 cbTaskRead = RTSgBufSegArrayCreate(&pIoCtx->SgBuf, aSeg, &cSegments, cbRead);
2886
2887 AssertMsg(cbTaskRead <= cbRead, ("Invalid number of bytes to read\n"));
2888
2889 LogFlow(("Reading %u bytes into %u segments\n", cbTaskRead, cSegments));
2890
2891#ifdef RT_STRICT
2892 for (unsigned i = 0; i < cSegments; i++)
2893 AssertMsg(aSeg[i].pvSeg && !(aSeg[i].cbSeg % 512),
2894 ("Segment %u is invalid\n", i));
2895#endif
2896
2897 PVDIOTASK pIoTask = vdIoTaskUserAlloc(pIoStorage, NULL, NULL, pIoCtx, cbTaskRead);
2898
2899 if (!pIoTask)
2900 return VERR_NO_MEMORY;
2901
2902 ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
2903
2904 void *pvTask;
2905 rc = pVDIo->pInterfaceIOCallbacks->pfnReadAsync(pVDIo->pInterfaceIO->pvUser,
2906 pIoStorage->pStorage,
2907 uOffset, aSeg, cSegments,
2908 cbTaskRead, pIoTask,
2909 &pvTask);
2910 if (RT_SUCCESS(rc))
2911 {
2912 AssertMsg(cbTaskRead <= pIoCtx->cbTransferLeft, ("Impossible!\n"));
2913 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbTaskRead);
2914 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
2915 vdIoTaskFree(pDisk, pIoTask);
2916 }
2917 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
2918 {
2919 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
2920 break;
2921 }
2922
2923 uOffset += cbTaskRead;
2924 cbRead -= cbTaskRead;
2925 }
2926
2927 LogFlowFunc(("returns rc=%Rrc\n", rc));
2928 return rc;
2929}
2930
2931static int vdIOIntWriteUserAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
2932 uint64_t uOffset, PVDIOCTX pIoCtx,
2933 size_t cbWrite,
2934 PFNVDXFERCOMPLETED pfnComplete,
2935 void *pvCompleteUser)
2936{
2937 int rc = VINF_SUCCESS;
2938 PVDIO pVDIo = (PVDIO)pvUser;
2939 PVBOXHDD pDisk = pVDIo->pDisk;
2940
2941 LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pIoCtx=%#p cbWrite=%u\n",
2942 pvUser, pIoStorage, uOffset, pIoCtx, cbWrite));
2943
2944 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
2945
2946 /* Build the S/G array and spawn a new I/O task */
2947 while (cbWrite)
2948 {
2949 RTSGSEG aSeg[VD_IO_TASK_SEGMENTS_MAX];
2950 unsigned cSegments = VD_IO_TASK_SEGMENTS_MAX;
2951 size_t cbTaskWrite = 0;
2952
2953 cbTaskWrite = RTSgBufSegArrayCreate(&pIoCtx->SgBuf, aSeg, &cSegments, cbWrite);
2954
2955 AssertMsg(cbTaskWrite <= cbWrite, ("Invalid number of bytes to write\n"));
2956
2957 LogFlow(("Writing %u bytes from %u segments\n", cbTaskWrite, cSegments));
2958
2959#ifdef DEBUG
2960 for (unsigned i = 0; i < cSegments; i++)
2961 AssertMsg(aSeg[i].pvSeg && !(aSeg[i].cbSeg % 512),
2962 ("Segment %u is invalid\n", i));
2963#endif
2964
2965 PVDIOTASK pIoTask = vdIoTaskUserAlloc(pIoStorage, pfnComplete, pvCompleteUser, pIoCtx, cbTaskWrite);
2966
2967 if (!pIoTask)
2968 return VERR_NO_MEMORY;
2969
2970 ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
2971
2972 void *pvTask;
2973 rc = pVDIo->pInterfaceIOCallbacks->pfnWriteAsync(pVDIo->pInterfaceIO->pvUser,
2974 pIoStorage->pStorage,
2975 uOffset, aSeg, cSegments,
2976 cbTaskWrite, pIoTask,
2977 &pvTask);
2978 if (RT_SUCCESS(rc))
2979 {
2980 AssertMsg(cbTaskWrite <= pIoCtx->cbTransferLeft, ("Impossible!\n"));
2981 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbTaskWrite);
2982 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
2983 vdIoTaskFree(pDisk, pIoTask);
2984 }
2985 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
2986 {
2987 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
2988 break;
2989 }
2990
2991 uOffset += cbTaskWrite;
2992 cbWrite -= cbTaskWrite;
2993 }
2994
2995 return rc;
2996}
2997
2998static int vdIOIntReadMetaAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
2999 uint64_t uOffset, void *pvBuf,
3000 size_t cbRead, PVDIOCTX pIoCtx,
3001 PPVDMETAXFER ppMetaXfer,
3002 PFNVDXFERCOMPLETED pfnComplete,
3003 void *pvCompleteUser)
3004{
3005 PVDIO pVDIo = (PVDIO)pvUser;
3006 PVBOXHDD pDisk = pVDIo->pDisk;
3007 int rc = VINF_SUCCESS;
3008 RTSGSEG Seg;
3009 PVDIOTASK pIoTask;
3010 PVDMETAXFER pMetaXfer = NULL;
3011 void *pvTask = NULL;
3012
3013 LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pvBuf=%#p cbRead=%u\n",
3014 pvUser, pIoStorage, uOffset, pvBuf, cbRead));
3015
3016 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3017
3018 pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGet(pIoStorage->pTreeMetaXfers, uOffset);
3019 if (!pMetaXfer)
3020 {
3021#ifdef RT_STRICT
3022 pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGetBestFit(pIoStorage->pTreeMetaXfers, uOffset, false /* fAbove */);
3023 AssertMsg(!pMetaXfer || (pMetaXfer->Core.Key + (RTFOFF)pMetaXfer->cbMeta <= (RTFOFF)uOffset),
3024 ("Overlapping meta transfers!\n"));
3025#endif
3026
3027 /* Allocate a new meta transfer. */
3028 pMetaXfer = vdMetaXferAlloc(pIoStorage, uOffset, cbRead);
3029 if (!pMetaXfer)
3030 return VERR_NO_MEMORY;
3031
3032 pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvCompleteUser, pMetaXfer);
3033 if (!pIoTask)
3034 {
3035 RTMemFree(pMetaXfer);
3036 return VERR_NO_MEMORY;
3037 }
3038
3039 Seg.cbSeg = cbRead;
3040 Seg.pvSeg = pMetaXfer->abData;
3041
3042 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_READ);
3043 rc = pVDIo->pInterfaceIOCallbacks->pfnReadAsync(pVDIo->pInterfaceIO->pvUser,
3044 pIoStorage->pStorage,
3045 uOffset, &Seg, 1,
3046 cbRead, pIoTask,
3047 &pvTask);
3048
3049 if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
3050 {
3051 bool fInserted = RTAvlrFileOffsetInsert(pIoStorage->pTreeMetaXfers, &pMetaXfer->Core);
3052 Assert(fInserted);
3053 }
3054 else
3055 RTMemFree(pMetaXfer);
3056
3057 if (RT_SUCCESS(rc))
3058 {
3059 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
3060 vdIoTaskFree(pDisk, pIoTask);
3061 }
3062 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS && !pfnComplete)
3063 rc = VERR_VD_NOT_ENOUGH_METADATA;
3064 }
3065
3066 Assert(VALID_PTR(pMetaXfer) || RT_FAILURE(rc));
3067
3068 if (RT_SUCCESS(rc) || rc == VERR_VD_NOT_ENOUGH_METADATA || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
3069 {
3070 /* If it is pending add the request to the list. */
3071 if (VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_READ)
3072 {
3073 PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
3074 AssertPtr(pDeferred);
3075
3076 RTListInit(&pDeferred->NodeDeferred);
3077 pDeferred->pIoCtx = pIoCtx;
3078
3079 ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
3080 RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
3081 rc = VERR_VD_NOT_ENOUGH_METADATA;
3082 }
3083 else
3084 {
3085 /* Transfer the data. */
3086 pMetaXfer->cRefs++;
3087 Assert(pMetaXfer->cbMeta >= cbRead);
3088 Assert(pMetaXfer->Core.Key == (RTFOFF)uOffset);
3089 memcpy(pvBuf, pMetaXfer->abData, cbRead);
3090 *ppMetaXfer = pMetaXfer;
3091 }
3092 }
3093
3094 return rc;
3095}
3096
3097static int vdIOIntWriteMetaAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
3098 uint64_t uOffset, void *pvBuf,
3099 size_t cbWrite, PVDIOCTX pIoCtx,
3100 PFNVDXFERCOMPLETED pfnComplete,
3101 void *pvCompleteUser)
3102{
3103 PVDIO pVDIo = (PVDIO)pvUser;
3104 PVBOXHDD pDisk = pVDIo->pDisk;
3105 int rc = VINF_SUCCESS;
3106 RTSGSEG Seg;
3107 PVDIOTASK pIoTask;
3108 PVDMETAXFER pMetaXfer = NULL;
3109 bool fInTree = false;
3110 void *pvTask = NULL;
3111
3112 LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pvBuf=%#p cbWrite=%u\n",
3113 pvUser, pIoStorage, uOffset, pvBuf, cbWrite));
3114
3115 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3116
3117 pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGet(pIoStorage->pTreeMetaXfers, uOffset);
3118 if (!pMetaXfer)
3119 {
3120 /* Allocate a new meta transfer. */
3121 pMetaXfer = vdMetaXferAlloc(pIoStorage, uOffset, cbWrite);
3122 if (!pMetaXfer)
3123 return VERR_NO_MEMORY;
3124 }
3125 else
3126 {
3127 Assert(pMetaXfer->cbMeta >= cbWrite);
3128 Assert(pMetaXfer->Core.Key == (RTFOFF)uOffset);
3129 fInTree = true;
3130 }
3131
3132 Assert(VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE);
3133
3134 pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvCompleteUser, pMetaXfer);
3135 if (!pIoTask)
3136 {
3137 RTMemFree(pMetaXfer);
3138 return VERR_NO_MEMORY;
3139 }
3140
3141 memcpy(pMetaXfer->abData, pvBuf, cbWrite);
3142 Seg.cbSeg = cbWrite;
3143 Seg.pvSeg = pMetaXfer->abData;
3144
3145 ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
3146
3147 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_WRITE);
3148 rc = pVDIo->pInterfaceIOCallbacks->pfnWriteAsync(pVDIo->pInterfaceIO->pvUser,
3149 pIoStorage->pStorage,
3150 uOffset, &Seg, 1,
3151 cbWrite, pIoTask,
3152 &pvTask);
3153 if (RT_SUCCESS(rc))
3154 {
3155 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
3156 ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
3157 vdIoTaskFree(pDisk, pIoTask);
3158 if (fInTree && !pMetaXfer->cRefs)
3159 {
3160 LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
3161 bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
3162 AssertMsg(fRemoved, ("Metadata transfer wasn't removed\n"));
3163 RTMemFree(pMetaXfer);
3164 pMetaXfer = NULL;
3165 }
3166 }
3167 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
3168 {
3169 PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
3170 AssertPtr(pDeferred);
3171
3172 RTListInit(&pDeferred->NodeDeferred);
3173 pDeferred->pIoCtx = pIoCtx;
3174
3175 if (!fInTree)
3176 {
3177 bool fInserted = RTAvlrFileOffsetInsert(pIoStorage->pTreeMetaXfers, &pMetaXfer->Core);
3178 Assert(fInserted);
3179 }
3180
3181 RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
3182 }
3183 else
3184 {
3185 RTMemFree(pMetaXfer);
3186 pMetaXfer = NULL;
3187 }
3188
3189 return rc;
3190}
3191
3192static void vdIOIntMetaXferRelease(void *pvUser, PVDMETAXFER pMetaXfer)
3193{
3194 PVDIO pVDIo = (PVDIO)pvUser;
3195 PVBOXHDD pDisk = pVDIo->pDisk;
3196 PVDIOSTORAGE pIoStorage = pMetaXfer->pIoStorage;
3197
3198 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3199
3200 Assert( VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE
3201 || VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_WRITE);
3202 Assert(pMetaXfer->cRefs > 0);
3203
3204 pMetaXfer->cRefs--;
3205 if ( !pMetaXfer->cRefs
3206 && RTListIsEmpty(&pMetaXfer->ListIoCtxWaiting)
3207 && VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE)
3208 {
3209 /* Free the meta data entry. */
3210 LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
3211 bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
3212 AssertMsg(fRemoved, ("Metadata transfer wasn't removed\n"));
3213
3214 RTMemFree(pMetaXfer);
3215 }
3216}
3217
3218static int vdIOIntFlushAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
3219 PVDIOCTX pIoCtx, PFNVDXFERCOMPLETED pfnComplete,
3220 void *pvCompleteUser)
3221{
3222 PVDIO pVDIo = (PVDIO)pvUser;
3223 PVBOXHDD pDisk = pVDIo->pDisk;
3224 int rc = VINF_SUCCESS;
3225 PVDIOTASK pIoTask;
3226 PVDMETAXFER pMetaXfer = NULL;
3227 void *pvTask = NULL;
3228
3229 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3230
3231 LogFlowFunc(("pvUser=%#p pIoStorage=%#p pIoCtx=%#p\n",
3232 pvUser, pIoStorage, pIoCtx));
3233
3234 /* Allocate a new meta transfer. */
3235 pMetaXfer = vdMetaXferAlloc(pIoStorage, 0, 0);
3236 if (!pMetaXfer)
3237 return VERR_NO_MEMORY;
3238
3239 pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvUser, pMetaXfer);
3240 if (!pIoTask)
3241 {
3242 RTMemFree(pMetaXfer);
3243 return VERR_NO_MEMORY;
3244 }
3245
3246 ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
3247
3248 PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
3249 AssertPtr(pDeferred);
3250
3251 RTListInit(&pDeferred->NodeDeferred);
3252 pDeferred->pIoCtx = pIoCtx;
3253
3254 RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
3255 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_FLUSH);
3256 rc = pVDIo->pInterfaceIOCallbacks->pfnFlushAsync(pVDIo->pInterfaceIO->pvUser,
3257 pIoStorage->pStorage,
3258 pIoTask, &pvTask);
3259 if (RT_SUCCESS(rc))
3260 {
3261 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
3262 ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
3263 vdIoTaskFree(pDisk, pIoTask);
3264 RTMemFree(pDeferred);
3265 RTMemFree(pMetaXfer);
3266 }
3267 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
3268 RTMemFree(pMetaXfer);
3269
3270 return rc;
3271}
3272
3273static size_t vdIOIntIoCtxCopyTo(void *pvUser, PVDIOCTX pIoCtx,
3274 void *pvBuf, size_t cbBuf)
3275{
3276 PVDIO pVDIo = (PVDIO)pvUser;
3277 PVBOXHDD pDisk = pVDIo->pDisk;
3278 size_t cbCopied = 0;
3279
3280 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3281
3282 cbCopied = vdIoCtxCopyTo(pIoCtx, (uint8_t *)pvBuf, cbBuf);
3283 Assert(cbCopied == cbBuf);
3284
3285 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbCopied);
3286
3287 return cbCopied;
3288}
3289
3290static size_t vdIOIntIoCtxCopyFrom(void *pvUser, PVDIOCTX pIoCtx,
3291 void *pvBuf, size_t cbBuf)
3292{
3293 PVDIO pVDIo = (PVDIO)pvUser;
3294 PVBOXHDD pDisk = pVDIo->pDisk;
3295 size_t cbCopied = 0;
3296
3297 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3298
3299 cbCopied = vdIoCtxCopyFrom(pIoCtx, (uint8_t *)pvBuf, cbBuf);
3300 Assert(cbCopied == cbBuf);
3301
3302 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbCopied);
3303
3304 return cbCopied;
3305}
3306
3307static size_t vdIOIntIoCtxSet(void *pvUser, PVDIOCTX pIoCtx, int ch, size_t cb)
3308{
3309 PVDIO pVDIo = (PVDIO)pvUser;
3310 PVBOXHDD pDisk = pVDIo->pDisk;
3311 size_t cbSet = 0;
3312
3313 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3314
3315 cbSet = vdIoCtxSet(pIoCtx, ch, cb);
3316 Assert(cbSet == cb);
3317
3318 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbSet);
3319
3320 return cbSet;
3321}
3322
3323static size_t vdIOIntIoCtxSegArrayCreate(void *pvUser, PVDIOCTX pIoCtx,
3324 PRTSGSEG paSeg, unsigned *pcSeg,
3325 size_t cbData)
3326{
3327 PVDIO pVDIo = (PVDIO)pvUser;
3328 PVBOXHDD pDisk = pVDIo->pDisk;
3329 size_t cbCreated = 0;
3330
3331 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3332
3333 cbCreated = RTSgBufSegArrayCreate(&pIoCtx->SgBuf, paSeg, pcSeg, cbData);
3334 Assert(!paSeg || cbData == cbCreated);
3335
3336 return cbCreated;
3337}
3338
3339static void vdIOIntIoCtxCompleted(void *pvUser, PVDIOCTX pIoCtx, int rcReq,
3340 size_t cbCompleted)
3341{
3342 /* Continue */
3343 pIoCtx->fBlocked = false;
3344 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbCompleted);
3345
3346 /* Clear the pointer to next transfer function in case we have nothing to transfer anymore.
3347 * @todo: Find a better way to prevent vdIoCtxContinue from calling the read/write helper again. */
3348 if (!pIoCtx->cbTransferLeft)
3349 pIoCtx->pfnIoCtxTransfer = NULL;
3350
3351 vdIoCtxContinue(pIoCtx, rcReq);
3352}
3353
3354/**
3355 * VD I/O interface callback for opening a file (limited version for VDGetFormat).
3356 */
3357static int vdIOIntOpenLimited(void *pvUser, const char *pszLocation,
3358 uint32_t fOpen, PPVDIOSTORAGE ppIoStorage)
3359{
3360 int rc = VINF_SUCCESS;
3361 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3362 PVDIOSTORAGE pIoStorage = (PVDIOSTORAGE)RTMemAllocZ(sizeof(VDIOSTORAGE));
3363
3364 if (!pIoStorage)
3365 return VERR_NO_MEMORY;
3366
3367 rc = pInterfaceIOCallbacks->pfnOpen(NULL, pszLocation, fOpen,
3368 NULL, &pIoStorage->pStorage);
3369 if (RT_SUCCESS(rc))
3370 *ppIoStorage = pIoStorage;
3371 else
3372 RTMemFree(pIoStorage);
3373
3374 return rc;
3375}
3376
3377static int vdIOIntCloseLimited(void *pvUser, PVDIOSTORAGE pIoStorage)
3378{
3379 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3380 int rc = pInterfaceIOCallbacks->pfnClose(NULL, pIoStorage->pStorage);
3381 AssertRC(rc);
3382
3383 RTMemFree(pIoStorage);
3384 return VINF_SUCCESS;
3385}
3386
3387static int vdIOIntDeleteLimited(void *pvUser, const char *pcszFilename)
3388{
3389 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3390 return pInterfaceIOCallbacks->pfnDelete(NULL, pcszFilename);
3391}
3392
3393static int vdIOIntMoveLimited(void *pvUser, const char *pcszSrc,
3394 const char *pcszDst, unsigned fMove)
3395{
3396 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3397 return pInterfaceIOCallbacks->pfnMove(NULL, pcszSrc, pcszDst, fMove);
3398}
3399
3400static int vdIOIntGetFreeSpaceLimited(void *pvUser, const char *pcszFilename,
3401 int64_t *pcbFreeSpace)
3402{
3403 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3404 return pInterfaceIOCallbacks->pfnGetFreeSpace(NULL, pcszFilename, pcbFreeSpace);
3405}
3406
3407static int vdIOIntGetModificationTimeLimited(void *pvUser,
3408 const char *pcszFilename,
3409 PRTTIMESPEC pModificationTime)
3410{
3411 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3412 return pInterfaceIOCallbacks->pfnGetModificationTime(NULL, pcszFilename, pModificationTime);
3413}
3414
3415static int vdIOIntGetSizeLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
3416 uint64_t *pcbSize)
3417{
3418 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3419 return pInterfaceIOCallbacks->pfnGetSize(NULL, pIoStorage->pStorage, pcbSize);
3420}
3421
3422static int vdIOIntSetSizeLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
3423 uint64_t cbSize)
3424{
3425 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3426 return pInterfaceIOCallbacks->pfnSetSize(NULL, pIoStorage->pStorage, cbSize);
3427}
3428
3429static int vdIOIntWriteSyncLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
3430 uint64_t uOffset, const void *pvBuf,
3431 size_t cbWrite, size_t *pcbWritten)
3432{
3433 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3434 return pInterfaceIOCallbacks->pfnWriteSync(NULL, pIoStorage->pStorage, uOffset, pvBuf, cbWrite, pcbWritten);
3435}
3436
3437static int vdIOIntReadSyncLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
3438 uint64_t uOffset, void *pvBuf, size_t cbRead,
3439 size_t *pcbRead)
3440{
3441 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3442 return pInterfaceIOCallbacks->pfnReadSync(NULL, pIoStorage->pStorage, uOffset, pvBuf, cbRead, pcbRead);
3443}
3444
3445static int vdIOIntFlushSyncLimited(void *pvUser, PVDIOSTORAGE pIoStorage)
3446{
3447 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3448 return pInterfaceIOCallbacks->pfnFlushSync(NULL, pIoStorage->pStorage);
3449}
3450
3451/**
3452 * internal: send output to the log (unconditionally).
3453 */
3454int vdLogMessage(void *pvUser, const char *pszFormat, va_list args)
3455{
3456 NOREF(pvUser);
3457 RTLogPrintfV(pszFormat, args);
3458 return VINF_SUCCESS;
3459}
3460
3461DECLINLINE(int) vdMessageWrapper(PVBOXHDD pDisk, const char *pszFormat, ...)
3462{
3463 va_list va;
3464 va_start(va, pszFormat);
3465 int rc = pDisk->pInterfaceErrorCallbacks->pfnMessage(pDisk->pInterfaceError->pvUser,
3466 pszFormat, va);
3467 va_end(va);
3468 return rc;
3469}
3470
3471
3472/**
3473 * Initializes HDD backends.
3474 *
3475 * @returns VBox status code.
3476 */
3477VBOXDDU_DECL(int) VDInit(void)
3478{
3479 int rc = vdAddBackends(aStaticBackends, RT_ELEMENTS(aStaticBackends));
3480 if (RT_SUCCESS(rc))
3481 {
3482 rc = vdAddCacheBackends(aStaticCacheBackends, RT_ELEMENTS(aStaticCacheBackends));
3483 if (RT_SUCCESS(rc))
3484 {
3485 rc = vdLoadDynamicBackends();
3486 if (RT_SUCCESS(rc))
3487 rc = vdLoadDynamicCacheBackends();
3488 }
3489 }
3490 LogRel(("VDInit finished\n"));
3491 return rc;
3492}
3493
3494/**
3495 * Destroys loaded HDD backends.
3496 *
3497 * @returns VBox status code.
3498 */
3499VBOXDDU_DECL(int) VDShutdown(void)
3500{
3501 PVBOXHDDBACKEND *pBackends = g_apBackends;
3502 PVDCACHEBACKEND *pCacheBackends = g_apCacheBackends;
3503 unsigned cBackends = g_cBackends;
3504
3505 if (!pBackends)
3506 return VERR_INTERNAL_ERROR;
3507
3508 g_cBackends = 0;
3509 g_apBackends = NULL;
3510
3511#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
3512 for (unsigned i = 0; i < cBackends; i++)
3513 if (pBackends[i]->hPlugin != NIL_RTLDRMOD)
3514 RTLdrClose(pBackends[i]->hPlugin);
3515#endif
3516
3517 /* Clear the supported cache backends. */
3518 cBackends = g_cCacheBackends;
3519 g_cCacheBackends = 0;
3520 g_apCacheBackends = NULL;
3521
3522#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
3523 for (unsigned i = 0; i < cBackends; i++)
3524 if (pCacheBackends[i]->hPlugin != NIL_RTLDRMOD)
3525 RTLdrClose(pCacheBackends[i]->hPlugin);
3526#endif
3527
3528 if (pCacheBackends)
3529 RTMemFree(pCacheBackends);
3530 RTMemFree(pBackends);
3531 return VINF_SUCCESS;
3532}
3533
3534
3535/**
3536 * Lists all HDD backends and their capabilities in a caller-provided buffer.
3537 *
3538 * @returns VBox status code.
3539 * VERR_BUFFER_OVERFLOW if not enough space is passed.
3540 * @param cEntriesAlloc Number of list entries available.
3541 * @param pEntries Pointer to array for the entries.
3542 * @param pcEntriesUsed Number of entries returned.
3543 */
3544VBOXDDU_DECL(int) VDBackendInfo(unsigned cEntriesAlloc, PVDBACKENDINFO pEntries,
3545 unsigned *pcEntriesUsed)
3546{
3547 int rc = VINF_SUCCESS;
3548 PRTDIR pPluginDir = NULL;
3549 unsigned cEntries = 0;
3550
3551 LogFlowFunc(("cEntriesAlloc=%u pEntries=%#p pcEntriesUsed=%#p\n", cEntriesAlloc, pEntries, pcEntriesUsed));
3552 /* Check arguments. */
3553 AssertMsgReturn(cEntriesAlloc,
3554 ("cEntriesAlloc=%u\n", cEntriesAlloc),
3555 VERR_INVALID_PARAMETER);
3556 AssertMsgReturn(VALID_PTR(pEntries),
3557 ("pEntries=%#p\n", pEntries),
3558 VERR_INVALID_PARAMETER);
3559 AssertMsgReturn(VALID_PTR(pcEntriesUsed),
3560 ("pcEntriesUsed=%#p\n", pcEntriesUsed),
3561 VERR_INVALID_PARAMETER);
3562 if (!g_apBackends)
3563 VDInit();
3564
3565 if (cEntriesAlloc < g_cBackends)
3566 {
3567 *pcEntriesUsed = g_cBackends;
3568 return VERR_BUFFER_OVERFLOW;
3569 }
3570
3571 for (unsigned i = 0; i < g_cBackends; i++)
3572 {
3573 pEntries[i].pszBackend = g_apBackends[i]->pszBackendName;
3574 pEntries[i].uBackendCaps = g_apBackends[i]->uBackendCaps;
3575 pEntries[i].paFileExtensions = g_apBackends[i]->paFileExtensions;
3576 pEntries[i].paConfigInfo = g_apBackends[i]->paConfigInfo;
3577 pEntries[i].pfnComposeLocation = g_apBackends[i]->pfnComposeLocation;
3578 pEntries[i].pfnComposeName = g_apBackends[i]->pfnComposeName;
3579 }
3580
3581 LogFlowFunc(("returns %Rrc *pcEntriesUsed=%u\n", rc, cEntries));
3582 *pcEntriesUsed = g_cBackends;
3583 return rc;
3584}
3585
3586/**
3587 * Lists the capabilities of a backend identified by its name.
3588 *
3589 * @returns VBox status code.
3590 * @param pszBackend The backend name.
3591 * @param pEntries Pointer to an entry.
3592 */
3593VBOXDDU_DECL(int) VDBackendInfoOne(const char *pszBackend, PVDBACKENDINFO pEntry)
3594{
3595 LogFlowFunc(("pszBackend=%#p pEntry=%#p\n", pszBackend, pEntry));
3596 /* Check arguments. */
3597 AssertMsgReturn(VALID_PTR(pszBackend),
3598 ("pszBackend=%#p\n", pszBackend),
3599 VERR_INVALID_PARAMETER);
3600 AssertMsgReturn(VALID_PTR(pEntry),
3601 ("pEntry=%#p\n", pEntry),
3602 VERR_INVALID_PARAMETER);
3603 if (!g_apBackends)
3604 VDInit();
3605
3606 /* Go through loaded backends. */
3607 for (unsigned i = 0; i < g_cBackends; i++)
3608 {
3609 if (!RTStrICmp(pszBackend, g_apBackends[i]->pszBackendName))
3610 {
3611 pEntry->pszBackend = g_apBackends[i]->pszBackendName;
3612 pEntry->uBackendCaps = g_apBackends[i]->uBackendCaps;
3613 pEntry->paFileExtensions = g_apBackends[i]->paFileExtensions;
3614 pEntry->paConfigInfo = g_apBackends[i]->paConfigInfo;
3615 return VINF_SUCCESS;
3616 }
3617 }
3618
3619 return VERR_NOT_FOUND;
3620}
3621
3622/**
3623 * Allocates and initializes an empty HDD container.
3624 * No image files are opened.
3625 *
3626 * @returns VBox status code.
3627 * @param pVDIfsDisk Pointer to the per-disk VD interface list.
3628 * @param enmType Type of the image container.
3629 * @param ppDisk Where to store the reference to HDD container.
3630 */
3631VBOXDDU_DECL(int) VDCreate(PVDINTERFACE pVDIfsDisk, VDTYPE enmType, PVBOXHDD *ppDisk)
3632{
3633 int rc = VINF_SUCCESS;
3634 PVBOXHDD pDisk = NULL;
3635
3636 LogFlowFunc(("pVDIfsDisk=%#p\n", pVDIfsDisk));
3637 do
3638 {
3639 /* Check arguments. */
3640 AssertMsgBreakStmt(VALID_PTR(ppDisk),
3641 ("ppDisk=%#p\n", ppDisk),
3642 rc = VERR_INVALID_PARAMETER);
3643
3644 pDisk = (PVBOXHDD)RTMemAllocZ(sizeof(VBOXHDD));
3645 if (pDisk)
3646 {
3647 pDisk->u32Signature = VBOXHDDDISK_SIGNATURE;
3648 pDisk->enmType = enmType;
3649 pDisk->cImages = 0;
3650 pDisk->pBase = NULL;
3651 pDisk->pLast = NULL;
3652 pDisk->cbSize = 0;
3653 pDisk->PCHSGeometry.cCylinders = 0;
3654 pDisk->PCHSGeometry.cHeads = 0;
3655 pDisk->PCHSGeometry.cSectors = 0;
3656 pDisk->LCHSGeometry.cCylinders = 0;
3657 pDisk->LCHSGeometry.cHeads = 0;
3658 pDisk->LCHSGeometry.cSectors = 0;
3659 pDisk->pVDIfsDisk = pVDIfsDisk;
3660 pDisk->pInterfaceError = NULL;
3661 pDisk->pInterfaceErrorCallbacks = NULL;
3662 pDisk->pInterfaceThreadSync = NULL;
3663 pDisk->pInterfaceThreadSyncCallbacks = NULL;
3664 pDisk->fLocked = false;
3665 RTListInit(&pDisk->ListWriteLocked);
3666
3667 /* Create the I/O ctx cache */
3668 rc = RTMemCacheCreate(&pDisk->hMemCacheIoCtx, sizeof(VDIOCTX), 0, UINT32_MAX,
3669 NULL, NULL, NULL, 0);
3670 if (RT_FAILURE(rc))
3671 {
3672 RTMemFree(pDisk);
3673 break;
3674 }
3675
3676 /* Create the I/O task cache */
3677 rc = RTMemCacheCreate(&pDisk->hMemCacheIoTask, sizeof(VDIOTASK), 0, UINT32_MAX,
3678 NULL, NULL, NULL, 0);
3679 if (RT_FAILURE(rc))
3680 {
3681 RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
3682 RTMemFree(pDisk);
3683 break;
3684 }
3685
3686 /* Create critical section. */
3687 rc = RTCritSectInit(&pDisk->CritSect);
3688 if (RT_FAILURE(rc))
3689 {
3690 RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
3691 RTMemCacheDestroy(pDisk->hMemCacheIoTask);
3692 RTMemFree(pDisk);
3693 break;
3694 }
3695
3696 pDisk->pInterfaceError = VDInterfaceGet(pVDIfsDisk, VDINTERFACETYPE_ERROR);
3697 if (pDisk->pInterfaceError)
3698 pDisk->pInterfaceErrorCallbacks = VDGetInterfaceError(pDisk->pInterfaceError);
3699
3700 pDisk->pInterfaceThreadSync = VDInterfaceGet(pVDIfsDisk, VDINTERFACETYPE_THREADSYNC);
3701 if (pDisk->pInterfaceThreadSync)
3702 pDisk->pInterfaceThreadSyncCallbacks = VDGetInterfaceThreadSync(pDisk->pInterfaceThreadSync);
3703
3704 /* Create fallback I/O callback table */
3705 pDisk->VDIIOCallbacks.cbSize = sizeof(VDINTERFACEIO);
3706 pDisk->VDIIOCallbacks.enmInterface = VDINTERFACETYPE_IO;
3707 pDisk->VDIIOCallbacks.pfnOpen = vdIOOpenFallback;
3708 pDisk->VDIIOCallbacks.pfnClose = vdIOCloseFallback;
3709 pDisk->VDIIOCallbacks.pfnDelete = vdIODeleteFallback;
3710 pDisk->VDIIOCallbacks.pfnMove = vdIOMoveFallback;
3711 pDisk->VDIIOCallbacks.pfnGetFreeSpace = vdIOGetFreeSpaceFallback;
3712 pDisk->VDIIOCallbacks.pfnGetModificationTime = vdIOGetModificationTimeFallback;
3713 pDisk->VDIIOCallbacks.pfnGetSize = vdIOGetSizeFallback;
3714 pDisk->VDIIOCallbacks.pfnSetSize = vdIOSetSizeFallback;
3715 pDisk->VDIIOCallbacks.pfnReadSync = vdIOReadSyncFallback;
3716 pDisk->VDIIOCallbacks.pfnWriteSync = vdIOWriteSyncFallback;
3717 pDisk->VDIIOCallbacks.pfnFlushSync = vdIOFlushSyncFallback;
3718 pDisk->VDIIOCallbacks.pfnReadAsync = vdIOReadAsyncFallback;
3719 pDisk->VDIIOCallbacks.pfnWriteAsync = vdIOWriteAsyncFallback;
3720 pDisk->VDIIOCallbacks.pfnFlushAsync = vdIOFlushAsyncFallback;
3721
3722 /*
3723 * Create the internal I/O callback table.
3724 * The interface is per-image but no need to duplicate the
3725 * callback table every time.
3726 */
3727 pDisk->VDIIOIntCallbacks.cbSize = sizeof(VDINTERFACEIOINT);
3728 pDisk->VDIIOIntCallbacks.enmInterface = VDINTERFACETYPE_IOINT;
3729 pDisk->VDIIOIntCallbacks.pfnOpen = vdIOIntOpen;
3730 pDisk->VDIIOIntCallbacks.pfnClose = vdIOIntClose;
3731 pDisk->VDIIOIntCallbacks.pfnDelete = vdIOIntDelete;
3732 pDisk->VDIIOIntCallbacks.pfnMove = vdIOIntMove;
3733 pDisk->VDIIOIntCallbacks.pfnGetFreeSpace = vdIOIntGetFreeSpace;
3734 pDisk->VDIIOIntCallbacks.pfnGetModificationTime = vdIOIntGetModificationTime;
3735 pDisk->VDIIOIntCallbacks.pfnGetSize = vdIOIntGetSize;
3736 pDisk->VDIIOIntCallbacks.pfnSetSize = vdIOIntSetSize;
3737 pDisk->VDIIOIntCallbacks.pfnReadSync = vdIOIntReadSync;
3738 pDisk->VDIIOIntCallbacks.pfnWriteSync = vdIOIntWriteSync;
3739 pDisk->VDIIOIntCallbacks.pfnFlushSync = vdIOIntFlushSync;
3740 pDisk->VDIIOIntCallbacks.pfnReadUserAsync = vdIOIntReadUserAsync;
3741 pDisk->VDIIOIntCallbacks.pfnWriteUserAsync = vdIOIntWriteUserAsync;
3742 pDisk->VDIIOIntCallbacks.pfnReadMetaAsync = vdIOIntReadMetaAsync;
3743 pDisk->VDIIOIntCallbacks.pfnWriteMetaAsync = vdIOIntWriteMetaAsync;
3744 pDisk->VDIIOIntCallbacks.pfnMetaXferRelease = vdIOIntMetaXferRelease;
3745 pDisk->VDIIOIntCallbacks.pfnFlushAsync = vdIOIntFlushAsync;
3746 pDisk->VDIIOIntCallbacks.pfnIoCtxCopyFrom = vdIOIntIoCtxCopyFrom;
3747 pDisk->VDIIOIntCallbacks.pfnIoCtxCopyTo = vdIOIntIoCtxCopyTo;
3748 pDisk->VDIIOIntCallbacks.pfnIoCtxSet = vdIOIntIoCtxSet;
3749 pDisk->VDIIOIntCallbacks.pfnIoCtxSegArrayCreate = vdIOIntIoCtxSegArrayCreate;
3750 pDisk->VDIIOIntCallbacks.pfnIoCtxCompleted = vdIOIntIoCtxCompleted;
3751
3752 *ppDisk = pDisk;
3753 }
3754 else
3755 {
3756 rc = VERR_NO_MEMORY;
3757 break;
3758 }
3759 } while (0);
3760
3761 LogFlowFunc(("returns %Rrc (pDisk=%#p)\n", rc, pDisk));
3762 return rc;
3763}
3764
3765/**
3766 * Destroys HDD container.
3767 * If container has opened image files they will be closed.
3768 *
3769 * @param pDisk Pointer to HDD container.
3770 */
3771VBOXDDU_DECL(void) VDDestroy(PVBOXHDD pDisk)
3772{
3773 LogFlowFunc(("pDisk=%#p\n", pDisk));
3774 do
3775 {
3776 /* sanity check */
3777 AssertPtrBreak(pDisk);
3778 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3779 VDCloseAll(pDisk);
3780 RTCritSectDelete(&pDisk->CritSect);
3781 RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
3782 RTMemCacheDestroy(pDisk->hMemCacheIoTask);
3783 RTMemFree(pDisk);
3784 } while (0);
3785 LogFlowFunc(("returns\n"));
3786}
3787
3788/**
3789 * Try to get the backend name which can use this image.
3790 *
3791 * @returns VBox status code.
3792 * VINF_SUCCESS if a plugin was found.
3793 * ppszFormat contains the string which can be used as backend name.
3794 * VERR_NOT_SUPPORTED if no backend was found.
3795 * @param pVDIfsDisk Pointer to the per-disk VD interface list.
3796 * @param pVDIfsImage Pointer to the per-image VD interface list.
3797 * @param pszFilename Name of the image file for which the backend is queried.
3798 * @param ppszFormat Receives pointer of the UTF-8 string which contains the format name.
3799 * The returned pointer must be freed using RTStrFree().
3800 */
3801VBOXDDU_DECL(int) VDGetFormat(PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
3802 const char *pszFilename, char **ppszFormat, VDTYPE *penmType)
3803{
3804 int rc = VERR_NOT_SUPPORTED;
3805 VDINTERFACEIOINT VDIIOIntCallbacks;
3806 VDINTERFACE VDIIOInt;
3807 VDINTERFACEIO VDIIOCallbacksFallback;
3808 PVDINTERFACE pInterfaceIO;
3809 PVDINTERFACEIO pInterfaceIOCallbacks;
3810
3811 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
3812 /* Check arguments. */
3813 AssertMsgReturn(VALID_PTR(pszFilename) && *pszFilename,
3814 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
3815 VERR_INVALID_PARAMETER);
3816 AssertMsgReturn(VALID_PTR(ppszFormat),
3817 ("ppszFormat=%#p\n", ppszFormat),
3818 VERR_INVALID_PARAMETER);
3819 AssertMsgReturn(VALID_PTR(ppszFormat),
3820 ("penmType=%#p\n", penmType),
3821 VERR_INVALID_PARAMETER);
3822
3823 if (!g_apBackends)
3824 VDInit();
3825
3826 pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
3827 if (!pInterfaceIO)
3828 {
3829 /*
3830 * Caller doesn't provide an I/O interface, create our own using the
3831 * native file API.
3832 */
3833 VDIIOCallbacksFallback.cbSize = sizeof(VDINTERFACEIO);
3834 VDIIOCallbacksFallback.enmInterface = VDINTERFACETYPE_IO;
3835 VDIIOCallbacksFallback.pfnOpen = vdIOOpenFallback;
3836 VDIIOCallbacksFallback.pfnClose = vdIOCloseFallback;
3837 VDIIOCallbacksFallback.pfnDelete = vdIODeleteFallback;
3838 VDIIOCallbacksFallback.pfnMove = vdIOMoveFallback;
3839 VDIIOCallbacksFallback.pfnGetFreeSpace = vdIOGetFreeSpaceFallback;
3840 VDIIOCallbacksFallback.pfnGetModificationTime = vdIOGetModificationTimeFallback;
3841 VDIIOCallbacksFallback.pfnGetSize = vdIOGetSizeFallback;
3842 VDIIOCallbacksFallback.pfnSetSize = vdIOSetSizeFallback;
3843 VDIIOCallbacksFallback.pfnReadSync = vdIOReadSyncFallback;
3844 VDIIOCallbacksFallback.pfnWriteSync = vdIOWriteSyncFallback;
3845 VDIIOCallbacksFallback.pfnFlushSync = vdIOFlushSyncFallback;
3846 pInterfaceIOCallbacks = &VDIIOCallbacksFallback;
3847 }
3848 else
3849 pInterfaceIOCallbacks = VDGetInterfaceIO(pInterfaceIO);
3850
3851 /* Set up the internal I/O interface. */
3852 AssertReturn(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
3853 VERR_INVALID_PARAMETER);
3854 VDIIOIntCallbacks.cbSize = sizeof(VDINTERFACEIOINT);
3855 VDIIOIntCallbacks.enmInterface = VDINTERFACETYPE_IOINT;
3856 VDIIOIntCallbacks.pfnOpen = vdIOIntOpenLimited;
3857 VDIIOIntCallbacks.pfnClose = vdIOIntCloseLimited;
3858 VDIIOIntCallbacks.pfnDelete = vdIOIntDeleteLimited;
3859 VDIIOIntCallbacks.pfnMove = vdIOIntMoveLimited;
3860 VDIIOIntCallbacks.pfnGetFreeSpace = vdIOIntGetFreeSpaceLimited;
3861 VDIIOIntCallbacks.pfnGetModificationTime = vdIOIntGetModificationTimeLimited;
3862 VDIIOIntCallbacks.pfnGetSize = vdIOIntGetSizeLimited;
3863 VDIIOIntCallbacks.pfnSetSize = vdIOIntSetSizeLimited;
3864 VDIIOIntCallbacks.pfnReadSync = vdIOIntReadSyncLimited;
3865 VDIIOIntCallbacks.pfnWriteSync = vdIOIntWriteSyncLimited;
3866 VDIIOIntCallbacks.pfnFlushSync = vdIOIntFlushSyncLimited;
3867 VDIIOIntCallbacks.pfnReadUserAsync = NULL;
3868 VDIIOIntCallbacks.pfnWriteUserAsync = NULL;
3869 VDIIOIntCallbacks.pfnReadMetaAsync = NULL;
3870 VDIIOIntCallbacks.pfnWriteMetaAsync = NULL;
3871 VDIIOIntCallbacks.pfnFlushAsync = NULL;
3872 rc = VDInterfaceAdd(&VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
3873 &VDIIOIntCallbacks, pInterfaceIOCallbacks, &pVDIfsImage);
3874 AssertRC(rc);
3875
3876 /* Find the backend supporting this file format. */
3877 for (unsigned i = 0; i < g_cBackends; i++)
3878 {
3879 if (g_apBackends[i]->pfnCheckIfValid)
3880 {
3881 rc = g_apBackends[i]->pfnCheckIfValid(pszFilename, pVDIfsDisk,
3882 pVDIfsImage, penmType);
3883 if ( RT_SUCCESS(rc)
3884 /* The correct backend has been found, but there is a small
3885 * incompatibility so that the file cannot be used. Stop here
3886 * and signal success - the actual open will of course fail,
3887 * but that will create a really sensible error message. */
3888 || ( rc != VERR_VD_GEN_INVALID_HEADER
3889 && rc != VERR_VD_VDI_INVALID_HEADER
3890 && rc != VERR_VD_VMDK_INVALID_HEADER
3891 && rc != VERR_VD_ISCSI_INVALID_HEADER
3892 && rc != VERR_VD_VHD_INVALID_HEADER
3893 && rc != VERR_VD_RAW_INVALID_HEADER
3894 && rc != VERR_VD_PARALLELS_INVALID_HEADER
3895 && rc != VERR_VD_DMG_INVALID_HEADER))
3896 {
3897 /* Copy the name into the new string. */
3898 char *pszFormat = RTStrDup(g_apBackends[i]->pszBackendName);
3899 if (!pszFormat)
3900 {
3901 rc = VERR_NO_MEMORY;
3902 break;
3903 }
3904 *ppszFormat = pszFormat;
3905 rc = VINF_SUCCESS;
3906 break;
3907 }
3908 rc = VERR_NOT_SUPPORTED;
3909 }
3910 }
3911
3912 /* Try the cache backends. */
3913 if (rc == VERR_NOT_SUPPORTED)
3914 {
3915 for (unsigned i = 0; i < g_cCacheBackends; i++)
3916 {
3917 if (g_apCacheBackends[i]->pfnProbe)
3918 {
3919 rc = g_apCacheBackends[i]->pfnProbe(pszFilename, pVDIfsDisk,
3920 pVDIfsImage);
3921 if ( RT_SUCCESS(rc)
3922 || (rc != VERR_VD_GEN_INVALID_HEADER))
3923 {
3924 /* Copy the name into the new string. */
3925 char *pszFormat = RTStrDup(g_apBackends[i]->pszBackendName);
3926 if (!pszFormat)
3927 {
3928 rc = VERR_NO_MEMORY;
3929 break;
3930 }
3931 *ppszFormat = pszFormat;
3932 rc = VINF_SUCCESS;
3933 break;
3934 }
3935 rc = VERR_NOT_SUPPORTED;
3936 }
3937 }
3938 }
3939
3940 LogFlowFunc(("returns %Rrc *ppszFormat=\"%s\"\n", rc, *ppszFormat));
3941 return rc;
3942}
3943
3944/**
3945 * Opens an image file.
3946 *
3947 * The first opened image file in HDD container must have a base image type,
3948 * others (next opened images) must be a differencing or undo images.
3949 * Linkage is checked for differencing image to be in consistence with the previously opened image.
3950 * When another differencing image is opened and the last image was opened in read/write access
3951 * mode, then the last image is reopened in read-only with deny write sharing mode. This allows
3952 * other processes to use images in read-only mode too.
3953 *
3954 * Note that the image is opened in read-only mode if a read/write open is not possible.
3955 * Use VDIsReadOnly to check open mode.
3956 *
3957 * @returns VBox status code.
3958 * @param pDisk Pointer to HDD container.
3959 * @param pszBackend Name of the image file backend to use.
3960 * @param pszFilename Name of the image file to open.
3961 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
3962 * @param pVDIfsImage Pointer to the per-image VD interface list.
3963 */
3964VBOXDDU_DECL(int) VDOpen(PVBOXHDD pDisk, const char *pszBackend,
3965 const char *pszFilename, unsigned uOpenFlags,
3966 PVDINTERFACE pVDIfsImage)
3967{
3968 int rc = VINF_SUCCESS;
3969 int rc2;
3970 bool fLockWrite = false;
3971 PVDIMAGE pImage = NULL;
3972
3973 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x, pVDIfsImage=%#p\n",
3974 pDisk, pszBackend, pszFilename, uOpenFlags, pVDIfsImage));
3975
3976 do
3977 {
3978 /* sanity check */
3979 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3980 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3981
3982 /* Check arguments. */
3983 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
3984 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
3985 rc = VERR_INVALID_PARAMETER);
3986 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
3987 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
3988 rc = VERR_INVALID_PARAMETER);
3989 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
3990 ("uOpenFlags=%#x\n", uOpenFlags),
3991 rc = VERR_INVALID_PARAMETER);
3992
3993 /* Set up image descriptor. */
3994 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
3995 if (!pImage)
3996 {
3997 rc = VERR_NO_MEMORY;
3998 break;
3999 }
4000 pImage->pszFilename = RTStrDup(pszFilename);
4001 if (!pImage->pszFilename)
4002 {
4003 rc = VERR_NO_MEMORY;
4004 break;
4005 }
4006
4007 pImage->VDIo.pDisk = pDisk;
4008 pImage->pVDIfsImage = pVDIfsImage;
4009
4010 rc = vdFindBackend(pszBackend, &pImage->Backend);
4011 if (RT_FAILURE(rc))
4012 break;
4013 if (!pImage->Backend)
4014 {
4015 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4016 N_("VD: unknown backend name '%s'"), pszBackend);
4017 break;
4018 }
4019
4020 /* Set up the I/O interface. */
4021 pImage->VDIo.pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
4022 if (pImage->VDIo.pInterfaceIO)
4023 pImage->VDIo.pInterfaceIOCallbacks = VDGetInterfaceIO(pImage->VDIo.pInterfaceIO);
4024 else
4025 {
4026 rc = VDInterfaceAdd(&pImage->VDIo.VDIIO, "VD_IO", VDINTERFACETYPE_IO,
4027 &pDisk->VDIIOCallbacks, pDisk, &pVDIfsImage);
4028 pImage->VDIo.pInterfaceIO = &pImage->VDIo.VDIIO;
4029 pImage->VDIo.pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
4030 }
4031
4032 /* Set up the internal I/O interface. */
4033 AssertBreakStmt(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
4034 rc = VERR_INVALID_PARAMETER);
4035 rc = VDInterfaceAdd(&pImage->VDIo.VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
4036 &pDisk->VDIIOIntCallbacks, &pImage->VDIo, &pImage->pVDIfsImage);
4037 AssertRC(rc);
4038
4039 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
4040 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
4041 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
4042 pDisk->pVDIfsDisk,
4043 pImage->pVDIfsImage,
4044 pDisk->enmType,
4045 &pImage->pBackendData);
4046 /* If the open in read-write mode failed, retry in read-only mode. */
4047 if (RT_FAILURE(rc))
4048 {
4049 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
4050 && ( rc == VERR_ACCESS_DENIED
4051 || rc == VERR_PERMISSION_DENIED
4052 || rc == VERR_WRITE_PROTECT
4053 || rc == VERR_SHARING_VIOLATION
4054 || rc == VERR_FILE_LOCK_FAILED))
4055 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
4056 (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
4057 | VD_OPEN_FLAGS_READONLY,
4058 pDisk->pVDIfsDisk,
4059 pImage->pVDIfsImage,
4060 pDisk->enmType,
4061 &pImage->pBackendData);
4062 if (RT_FAILURE(rc))
4063 {
4064 rc = vdError(pDisk, rc, RT_SRC_POS,
4065 N_("VD: error %Rrc opening image file '%s'"), rc, pszFilename);
4066 break;
4067 }
4068 }
4069
4070 /* Lock disk for writing, as we modify pDisk information below. */
4071 rc2 = vdThreadStartWrite(pDisk);
4072 AssertRC(rc2);
4073 fLockWrite = true;
4074
4075 pImage->VDIo.pBackendData = pImage->pBackendData;
4076
4077 /* Check image type. As the image itself has only partial knowledge
4078 * whether it's a base image or not, this info is derived here. The
4079 * base image can be fixed or normal, all others must be normal or
4080 * diff images. Some image formats don't distinguish between normal
4081 * and diff images, so this must be corrected here. */
4082 unsigned uImageFlags;
4083 uImageFlags = pImage->Backend->pfnGetImageFlags(pImage->pBackendData);
4084 if (RT_FAILURE(rc))
4085 uImageFlags = VD_IMAGE_FLAGS_NONE;
4086 if ( RT_SUCCESS(rc)
4087 && !(uOpenFlags & VD_OPEN_FLAGS_INFO))
4088 {
4089 if ( pDisk->cImages == 0
4090 && (uImageFlags & VD_IMAGE_FLAGS_DIFF))
4091 {
4092 rc = VERR_VD_INVALID_TYPE;
4093 break;
4094 }
4095 else if (pDisk->cImages != 0)
4096 {
4097 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
4098 {
4099 rc = VERR_VD_INVALID_TYPE;
4100 break;
4101 }
4102 else
4103 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
4104 }
4105 }
4106
4107 /* Ensure we always get correct diff information, even if the backend
4108 * doesn't actually have a stored flag for this. It must not return
4109 * bogus information for the parent UUID if it is not a diff image. */
4110 RTUUID parentUuid;
4111 RTUuidClear(&parentUuid);
4112 rc2 = pImage->Backend->pfnGetParentUuid(pImage->pBackendData, &parentUuid);
4113 if (RT_SUCCESS(rc2) && !RTUuidIsNull(&parentUuid))
4114 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
4115
4116 pImage->uImageFlags = uImageFlags;
4117
4118 /* Force sane optimization settings. It's not worth avoiding writes
4119 * to fixed size images. The overhead would have almost no payback. */
4120 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
4121 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
4122
4123 /** @todo optionally check UUIDs */
4124
4125 /* Cache disk information. */
4126 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
4127
4128 /* Cache PCHS geometry. */
4129 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
4130 &pDisk->PCHSGeometry);
4131 if (RT_FAILURE(rc2))
4132 {
4133 pDisk->PCHSGeometry.cCylinders = 0;
4134 pDisk->PCHSGeometry.cHeads = 0;
4135 pDisk->PCHSGeometry.cSectors = 0;
4136 }
4137 else
4138 {
4139 /* Make sure the PCHS geometry is properly clipped. */
4140 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
4141 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
4142 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
4143 }
4144
4145 /* Cache LCHS geometry. */
4146 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
4147 &pDisk->LCHSGeometry);
4148 if (RT_FAILURE(rc2))
4149 {
4150 pDisk->LCHSGeometry.cCylinders = 0;
4151 pDisk->LCHSGeometry.cHeads = 0;
4152 pDisk->LCHSGeometry.cSectors = 0;
4153 }
4154 else
4155 {
4156 /* Make sure the LCHS geometry is properly clipped. */
4157 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
4158 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
4159 }
4160
4161 if (pDisk->cImages != 0)
4162 {
4163 /* Switch previous image to read-only mode. */
4164 unsigned uOpenFlagsPrevImg;
4165 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
4166 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
4167 {
4168 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
4169 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pBackendData, uOpenFlagsPrevImg);
4170 }
4171 }
4172
4173 if (RT_SUCCESS(rc))
4174 {
4175 /* Image successfully opened, make it the last image. */
4176 vdAddImageToList(pDisk, pImage);
4177 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
4178 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
4179 }
4180 else
4181 {
4182 /* Error detected, but image opened. Close image. */
4183 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, false);
4184 AssertRC(rc2);
4185 pImage->pBackendData = NULL;
4186 }
4187 } while (0);
4188
4189 if (RT_UNLIKELY(fLockWrite))
4190 {
4191 rc2 = vdThreadFinishWrite(pDisk);
4192 AssertRC(rc2);
4193 }
4194
4195 if (RT_FAILURE(rc))
4196 {
4197 if (pImage)
4198 {
4199 if (pImage->pszFilename)
4200 RTStrFree(pImage->pszFilename);
4201 RTMemFree(pImage);
4202 }
4203 }
4204
4205 LogFlowFunc(("returns %Rrc\n", rc));
4206 return rc;
4207}
4208
4209/**
4210 * Opens a cache image.
4211 *
4212 * @return VBox status code.
4213 * @param pDisk Pointer to the HDD container which should use the cache image.
4214 * @param pszBackend Name of the cache file backend to use (case insensitive).
4215 * @param pszFilename Name of the cache image to open.
4216 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
4217 * @param pVDIfsCache Pointer to the per-cache VD interface list.
4218 */
4219VBOXDDU_DECL(int) VDCacheOpen(PVBOXHDD pDisk, const char *pszBackend,
4220 const char *pszFilename, unsigned uOpenFlags,
4221 PVDINTERFACE pVDIfsCache)
4222{
4223 int rc = VINF_SUCCESS;
4224 int rc2;
4225 bool fLockWrite = false;
4226 PVDCACHE pCache = NULL;
4227
4228 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x, pVDIfsCache=%#p\n",
4229 pDisk, pszBackend, pszFilename, uOpenFlags, pVDIfsCache));
4230
4231 do
4232 {
4233 /* sanity check */
4234 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4235 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4236
4237 /* Check arguments. */
4238 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
4239 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
4240 rc = VERR_INVALID_PARAMETER);
4241 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
4242 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
4243 rc = VERR_INVALID_PARAMETER);
4244 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
4245 ("uOpenFlags=%#x\n", uOpenFlags),
4246 rc = VERR_INVALID_PARAMETER);
4247
4248 /* Set up image descriptor. */
4249 pCache = (PVDCACHE)RTMemAllocZ(sizeof(VDCACHE));
4250 if (!pCache)
4251 {
4252 rc = VERR_NO_MEMORY;
4253 break;
4254 }
4255 pCache->pszFilename = RTStrDup(pszFilename);
4256 if (!pCache->pszFilename)
4257 {
4258 rc = VERR_NO_MEMORY;
4259 break;
4260 }
4261
4262 pCache->VDIo.pDisk = pDisk;
4263 pCache->pVDIfsCache = pVDIfsCache;
4264
4265 rc = vdFindCacheBackend(pszBackend, &pCache->Backend);
4266 if (RT_FAILURE(rc))
4267 break;
4268 if (!pCache->Backend)
4269 {
4270 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4271 N_("VD: unknown backend name '%s'"), pszBackend);
4272 break;
4273 }
4274
4275 /* Set up the I/O interface. */
4276 pCache->VDIo.pInterfaceIO = VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IO);
4277 if (pCache->VDIo.pInterfaceIO)
4278 pCache->VDIo.pInterfaceIOCallbacks = VDGetInterfaceIO(pCache->VDIo.pInterfaceIO);
4279 else
4280 {
4281 rc = VDInterfaceAdd(&pCache->VDIo.VDIIO, "VD_IO", VDINTERFACETYPE_IO,
4282 &pDisk->VDIIOCallbacks, pDisk, &pVDIfsCache);
4283 pCache->VDIo.pInterfaceIO = &pCache->VDIo.VDIIO;
4284 pCache->VDIo.pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
4285 }
4286
4287 /* Set up the internal I/O interface. */
4288 AssertBreakStmt(!VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IOINT),
4289 rc = VERR_INVALID_PARAMETER);
4290 rc = VDInterfaceAdd(&pCache->VDIo.VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
4291 &pDisk->VDIIOIntCallbacks, &pCache->VDIo, &pCache->pVDIfsCache);
4292 AssertRC(rc);
4293
4294 pCache->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
4295 rc = pCache->Backend->pfnOpen(pCache->pszFilename,
4296 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
4297 pDisk->pVDIfsDisk,
4298 pCache->pVDIfsCache,
4299 &pCache->pBackendData);
4300 /* If the open in read-write mode failed, retry in read-only mode. */
4301 if (RT_FAILURE(rc))
4302 {
4303 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
4304 && ( rc == VERR_ACCESS_DENIED
4305 || rc == VERR_PERMISSION_DENIED
4306 || rc == VERR_WRITE_PROTECT
4307 || rc == VERR_SHARING_VIOLATION
4308 || rc == VERR_FILE_LOCK_FAILED))
4309 rc = pCache->Backend->pfnOpen(pCache->pszFilename,
4310 (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
4311 | VD_OPEN_FLAGS_READONLY,
4312 pDisk->pVDIfsDisk,
4313 pCache->pVDIfsCache,
4314 &pCache->pBackendData);
4315 if (RT_FAILURE(rc))
4316 {
4317 rc = vdError(pDisk, rc, RT_SRC_POS,
4318 N_("VD: error %Rrc opening image file '%s'"), rc, pszFilename);
4319 break;
4320 }
4321 }
4322
4323 /* Lock disk for writing, as we modify pDisk information below. */
4324 rc2 = vdThreadStartWrite(pDisk);
4325 AssertRC(rc2);
4326 fLockWrite = true;
4327
4328 /*
4329 * Check that the modification UUID of the cache and last image
4330 * match. If not the image was modified in-between without the cache.
4331 * The cache might contain stale data.
4332 */
4333 RTUUID UuidImage, UuidCache;
4334
4335 rc = pCache->Backend->pfnGetModificationUuid(pCache->pBackendData,
4336 &UuidCache);
4337 if (RT_SUCCESS(rc))
4338 {
4339 rc = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
4340 &UuidImage);
4341 if (RT_SUCCESS(rc))
4342 {
4343 if (RTUuidCompare(&UuidImage, &UuidCache))
4344 rc = VERR_VD_CACHE_NOT_UP_TO_DATE;
4345 }
4346 }
4347
4348 /*
4349 * We assume that the user knows what he is doing if one of the images
4350 * doesn't support the modification uuid.
4351 */
4352 if (rc == VERR_NOT_SUPPORTED)
4353 rc = VINF_SUCCESS;
4354
4355 if (RT_SUCCESS(rc))
4356 {
4357 /* Cache successfully opened, make it the current one. */
4358 if (!pDisk->pCache)
4359 pDisk->pCache = pCache;
4360 else
4361 rc = VERR_VD_CACHE_ALREADY_EXISTS;
4362 }
4363
4364 if (RT_FAILURE(rc))
4365 {
4366 /* Error detected, but image opened. Close image. */
4367 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, false);
4368 AssertRC(rc2);
4369 pCache->pBackendData = NULL;
4370 }
4371 } while (0);
4372
4373 if (RT_UNLIKELY(fLockWrite))
4374 {
4375 rc2 = vdThreadFinishWrite(pDisk);
4376 AssertRC(rc2);
4377 }
4378
4379 if (RT_FAILURE(rc))
4380 {
4381 if (pCache)
4382 {
4383 if (pCache->pszFilename)
4384 RTStrFree(pCache->pszFilename);
4385 RTMemFree(pCache);
4386 }
4387 }
4388
4389 LogFlowFunc(("returns %Rrc\n", rc));
4390 return rc;
4391}
4392
4393/**
4394 * Creates and opens a new base image file.
4395 *
4396 * @returns VBox status code.
4397 * @param pDisk Pointer to HDD container.
4398 * @param pszBackend Name of the image file backend to use.
4399 * @param pszFilename Name of the image file to create.
4400 * @param cbSize Image size in bytes.
4401 * @param uImageFlags Flags specifying special image features.
4402 * @param pszComment Pointer to image comment. NULL is ok.
4403 * @param pPCHSGeometry Pointer to physical disk geometry <= (16383,16,63). Not NULL.
4404 * @param pLCHSGeometry Pointer to logical disk geometry <= (x,255,63). Not NULL.
4405 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
4406 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
4407 * @param pVDIfsImage Pointer to the per-image VD interface list.
4408 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
4409 */
4410VBOXDDU_DECL(int) VDCreateBase(PVBOXHDD pDisk, const char *pszBackend,
4411 const char *pszFilename, uint64_t cbSize,
4412 unsigned uImageFlags, const char *pszComment,
4413 PCVDGEOMETRY pPCHSGeometry,
4414 PCVDGEOMETRY pLCHSGeometry,
4415 PCRTUUID pUuid, unsigned uOpenFlags,
4416 PVDINTERFACE pVDIfsImage,
4417 PVDINTERFACE pVDIfsOperation)
4418{
4419 int rc = VINF_SUCCESS;
4420 int rc2;
4421 bool fLockWrite = false, fLockRead = false;
4422 PVDIMAGE pImage = NULL;
4423 RTUUID uuid;
4424
4425 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",
4426 pDisk, pszBackend, pszFilename, cbSize, uImageFlags, pszComment,
4427 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
4428 pPCHSGeometry->cSectors, pLCHSGeometry->cCylinders,
4429 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors, pUuid,
4430 uOpenFlags, pVDIfsImage, pVDIfsOperation));
4431
4432 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
4433 VDINTERFACETYPE_PROGRESS);
4434 PVDINTERFACEPROGRESS pCbProgress = NULL;
4435 if (pIfProgress)
4436 pCbProgress = VDGetInterfaceProgress(pIfProgress);
4437
4438 do
4439 {
4440 /* sanity check */
4441 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4442 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4443
4444 /* Check arguments. */
4445 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
4446 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
4447 rc = VERR_INVALID_PARAMETER);
4448 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
4449 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
4450 rc = VERR_INVALID_PARAMETER);
4451 AssertMsgBreakStmt(cbSize,
4452 ("cbSize=%llu\n", cbSize),
4453 rc = VERR_INVALID_PARAMETER);
4454 AssertMsgBreakStmt( ((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0)
4455 || ((uImageFlags & (VD_IMAGE_FLAGS_FIXED | VD_IMAGE_FLAGS_DIFF)) != VD_IMAGE_FLAGS_FIXED),
4456 ("uImageFlags=%#x\n", uImageFlags),
4457 rc = VERR_INVALID_PARAMETER);
4458 /* The PCHS geometry fields may be 0 to leave it for later. */
4459 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
4460 && pPCHSGeometry->cHeads <= 16
4461 && pPCHSGeometry->cSectors <= 63,
4462 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
4463 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
4464 pPCHSGeometry->cSectors),
4465 rc = VERR_INVALID_PARAMETER);
4466 /* The LCHS geometry fields may be 0 to leave it to later autodetection. */
4467 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
4468 && pLCHSGeometry->cHeads <= 255
4469 && pLCHSGeometry->cSectors <= 63,
4470 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
4471 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
4472 pLCHSGeometry->cSectors),
4473 rc = VERR_INVALID_PARAMETER);
4474 /* The UUID may be NULL. */
4475 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
4476 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
4477 rc = VERR_INVALID_PARAMETER);
4478 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
4479 ("uOpenFlags=%#x\n", uOpenFlags),
4480 rc = VERR_INVALID_PARAMETER);
4481
4482 /* Check state. Needs a temporary read lock. Holding the write lock
4483 * all the time would be blocking other activities for too long. */
4484 rc2 = vdThreadStartRead(pDisk);
4485 AssertRC(rc2);
4486 fLockRead = true;
4487 AssertMsgBreakStmt(pDisk->cImages == 0,
4488 ("Create base image cannot be done with other images open\n"),
4489 rc = VERR_VD_INVALID_STATE);
4490 rc2 = vdThreadFinishRead(pDisk);
4491 AssertRC(rc2);
4492 fLockRead = false;
4493
4494 /* Set up image descriptor. */
4495 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
4496 if (!pImage)
4497 {
4498 rc = VERR_NO_MEMORY;
4499 break;
4500 }
4501 pImage->pszFilename = RTStrDup(pszFilename);
4502 if (!pImage->pszFilename)
4503 {
4504 rc = VERR_NO_MEMORY;
4505 break;
4506 }
4507 pImage->VDIo.pDisk = pDisk;
4508 pImage->pVDIfsImage = pVDIfsImage;
4509
4510 /* Set up the I/O interface. */
4511 pImage->VDIo.pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
4512 if (pImage->VDIo.pInterfaceIO)
4513 pImage->VDIo.pInterfaceIOCallbacks = VDGetInterfaceIO(pImage->VDIo.pInterfaceIO);
4514 else
4515 {
4516 rc = VDInterfaceAdd(&pImage->VDIo.VDIIO, "VD_IO", VDINTERFACETYPE_IO,
4517 &pDisk->VDIIOCallbacks, pDisk, &pVDIfsImage);
4518 pImage->VDIo.pInterfaceIO = &pImage->VDIo.VDIIO;
4519 pImage->VDIo.pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
4520 }
4521
4522 /* Set up the internal I/O interface. */
4523 AssertBreakStmt(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
4524 rc = VERR_INVALID_PARAMETER);
4525 rc = VDInterfaceAdd(&pImage->VDIo.VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
4526 &pDisk->VDIIOIntCallbacks, &pImage->VDIo, &pImage->pVDIfsImage);
4527 AssertRC(rc);
4528
4529 rc = vdFindBackend(pszBackend, &pImage->Backend);
4530 if (RT_FAILURE(rc))
4531 break;
4532 if (!pImage->Backend)
4533 {
4534 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4535 N_("VD: unknown backend name '%s'"), pszBackend);
4536 break;
4537 }
4538 if (!(pImage->Backend->uBackendCaps & ( VD_CAP_CREATE_FIXED
4539 | VD_CAP_CREATE_DYNAMIC)))
4540 {
4541 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4542 N_("VD: backend '%s' cannot create base images"), pszBackend);
4543 break;
4544 }
4545
4546 /* Create UUID if the caller didn't specify one. */
4547 if (!pUuid)
4548 {
4549 rc = RTUuidCreate(&uuid);
4550 if (RT_FAILURE(rc))
4551 {
4552 rc = vdError(pDisk, rc, RT_SRC_POS,
4553 N_("VD: cannot generate UUID for image '%s'"),
4554 pszFilename);
4555 break;
4556 }
4557 pUuid = &uuid;
4558 }
4559
4560 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
4561 uImageFlags &= ~VD_IMAGE_FLAGS_DIFF;
4562 rc = pImage->Backend->pfnCreate(pImage->pszFilename, cbSize,
4563 uImageFlags, pszComment, pPCHSGeometry,
4564 pLCHSGeometry, pUuid,
4565 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
4566 0, 99,
4567 pDisk->pVDIfsDisk,
4568 pImage->pVDIfsImage,
4569 pVDIfsOperation,
4570 &pImage->pBackendData);
4571
4572 if (RT_SUCCESS(rc))
4573 {
4574 pImage->VDIo.pBackendData = pImage->pBackendData;
4575 pImage->uImageFlags = uImageFlags;
4576
4577 /* Force sane optimization settings. It's not worth avoiding writes
4578 * to fixed size images. The overhead would have almost no payback. */
4579 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
4580 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
4581
4582 /* Lock disk for writing, as we modify pDisk information below. */
4583 rc2 = vdThreadStartWrite(pDisk);
4584 AssertRC(rc2);
4585 fLockWrite = true;
4586
4587 /** @todo optionally check UUIDs */
4588
4589 /* Re-check state, as the lock wasn't held and another image
4590 * creation call could have been done by another thread. */
4591 AssertMsgStmt(pDisk->cImages == 0,
4592 ("Create base image cannot be done with other images open\n"),
4593 rc = VERR_VD_INVALID_STATE);
4594 }
4595
4596 if (RT_SUCCESS(rc))
4597 {
4598 /* Cache disk information. */
4599 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
4600
4601 /* Cache PCHS geometry. */
4602 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
4603 &pDisk->PCHSGeometry);
4604 if (RT_FAILURE(rc2))
4605 {
4606 pDisk->PCHSGeometry.cCylinders = 0;
4607 pDisk->PCHSGeometry.cHeads = 0;
4608 pDisk->PCHSGeometry.cSectors = 0;
4609 }
4610 else
4611 {
4612 /* Make sure the CHS geometry is properly clipped. */
4613 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
4614 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
4615 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
4616 }
4617
4618 /* Cache LCHS geometry. */
4619 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
4620 &pDisk->LCHSGeometry);
4621 if (RT_FAILURE(rc2))
4622 {
4623 pDisk->LCHSGeometry.cCylinders = 0;
4624 pDisk->LCHSGeometry.cHeads = 0;
4625 pDisk->LCHSGeometry.cSectors = 0;
4626 }
4627 else
4628 {
4629 /* Make sure the CHS geometry is properly clipped. */
4630 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
4631 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
4632 }
4633
4634 /* Image successfully opened, make it the last image. */
4635 vdAddImageToList(pDisk, pImage);
4636 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
4637 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
4638 }
4639 else
4640 {
4641 /* Error detected, image may or may not be opened. Close and delete
4642 * image if it was opened. */
4643 if (pImage->pBackendData)
4644 {
4645 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, true);
4646 AssertRC(rc2);
4647 pImage->pBackendData = NULL;
4648 }
4649 }
4650 } while (0);
4651
4652 if (RT_UNLIKELY(fLockWrite))
4653 {
4654 rc2 = vdThreadFinishWrite(pDisk);
4655 AssertRC(rc2);
4656 }
4657 else if (RT_UNLIKELY(fLockRead))
4658 {
4659 rc2 = vdThreadFinishRead(pDisk);
4660 AssertRC(rc2);
4661 }
4662
4663 if (RT_FAILURE(rc))
4664 {
4665 if (pImage)
4666 {
4667 if (pImage->pszFilename)
4668 RTStrFree(pImage->pszFilename);
4669 RTMemFree(pImage);
4670 }
4671 }
4672
4673 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
4674 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
4675
4676 LogFlowFunc(("returns %Rrc\n", rc));
4677 return rc;
4678}
4679
4680/**
4681 * Creates and opens a new differencing image file in HDD container.
4682 * See comments for VDOpen function about differencing images.
4683 *
4684 * @returns VBox status code.
4685 * @param pDisk Pointer to HDD container.
4686 * @param pszBackend Name of the image file backend to use.
4687 * @param pszFilename Name of the differencing image file to create.
4688 * @param uImageFlags Flags specifying special image features.
4689 * @param pszComment Pointer to image comment. NULL is ok.
4690 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
4691 * @param pParentUuid New parent UUID of the image. If NULL, the UUID is queried automatically.
4692 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
4693 * @param pVDIfsImage Pointer to the per-image VD interface list.
4694 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
4695 */
4696VBOXDDU_DECL(int) VDCreateDiff(PVBOXHDD pDisk, const char *pszBackend,
4697 const char *pszFilename, unsigned uImageFlags,
4698 const char *pszComment, PCRTUUID pUuid,
4699 PCRTUUID pParentUuid, unsigned uOpenFlags,
4700 PVDINTERFACE pVDIfsImage,
4701 PVDINTERFACE pVDIfsOperation)
4702{
4703 int rc = VINF_SUCCESS;
4704 int rc2;
4705 bool fLockWrite = false, fLockRead = false;
4706 PVDIMAGE pImage = NULL;
4707 RTUUID uuid;
4708
4709 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
4710 pDisk, pszBackend, pszFilename, uImageFlags, pszComment, pUuid, uOpenFlags, pVDIfsImage, pVDIfsOperation));
4711
4712 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
4713 VDINTERFACETYPE_PROGRESS);
4714 PVDINTERFACEPROGRESS pCbProgress = NULL;
4715 if (pIfProgress)
4716 pCbProgress = VDGetInterfaceProgress(pIfProgress);
4717
4718 do
4719 {
4720 /* sanity check */
4721 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4722 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4723
4724 /* Check arguments. */
4725 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
4726 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
4727 rc = VERR_INVALID_PARAMETER);
4728 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
4729 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
4730 rc = VERR_INVALID_PARAMETER);
4731 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
4732 ("uImageFlags=%#x\n", uImageFlags),
4733 rc = VERR_INVALID_PARAMETER);
4734 /* The UUID may be NULL. */
4735 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
4736 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
4737 rc = VERR_INVALID_PARAMETER);
4738 /* The parent UUID may be NULL. */
4739 AssertMsgBreakStmt(pParentUuid == NULL || VALID_PTR(pParentUuid),
4740 ("pParentUuid=%#p ParentUUID=%RTuuid\n", pParentUuid, pParentUuid),
4741 rc = VERR_INVALID_PARAMETER);
4742 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
4743 ("uOpenFlags=%#x\n", uOpenFlags),
4744 rc = VERR_INVALID_PARAMETER);
4745
4746 /* Check state. Needs a temporary read lock. Holding the write lock
4747 * all the time would be blocking other activities for too long. */
4748 rc2 = vdThreadStartRead(pDisk);
4749 AssertRC(rc2);
4750 fLockRead = true;
4751 AssertMsgBreakStmt(pDisk->cImages != 0,
4752 ("Create diff image cannot be done without other images open\n"),
4753 rc = VERR_VD_INVALID_STATE);
4754 rc2 = vdThreadFinishRead(pDisk);
4755 AssertRC(rc2);
4756 fLockRead = false;
4757
4758 /* Set up image descriptor. */
4759 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
4760 if (!pImage)
4761 {
4762 rc = VERR_NO_MEMORY;
4763 break;
4764 }
4765 pImage->pszFilename = RTStrDup(pszFilename);
4766 if (!pImage->pszFilename)
4767 {
4768 rc = VERR_NO_MEMORY;
4769 break;
4770 }
4771
4772 rc = vdFindBackend(pszBackend, &pImage->Backend);
4773 if (RT_FAILURE(rc))
4774 break;
4775 if (!pImage->Backend)
4776 {
4777 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4778 N_("VD: unknown backend name '%s'"), pszBackend);
4779 break;
4780 }
4781 if ( !(pImage->Backend->uBackendCaps & VD_CAP_DIFF)
4782 || !(pImage->Backend->uBackendCaps & ( VD_CAP_CREATE_FIXED
4783 | VD_CAP_CREATE_DYNAMIC)))
4784 {
4785 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4786 N_("VD: backend '%s' cannot create diff images"), pszBackend);
4787 break;
4788 }
4789
4790 pImage->VDIo.pDisk = pDisk;
4791 pImage->pVDIfsImage = pVDIfsImage;
4792
4793 /* Set up the I/O interface. */
4794 pImage->VDIo.pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
4795 if (pImage->VDIo.pInterfaceIO)
4796 pImage->VDIo.pInterfaceIOCallbacks = VDGetInterfaceIO(pImage->VDIo.pInterfaceIO);
4797 else
4798 {
4799 rc = VDInterfaceAdd(&pImage->VDIo.VDIIO, "VD_IO", VDINTERFACETYPE_IO,
4800 &pDisk->VDIIOCallbacks, pDisk, &pVDIfsImage);
4801 pImage->VDIo.pInterfaceIO = &pImage->VDIo.VDIIO;
4802 pImage->VDIo.pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
4803 }
4804
4805 /* Set up the internal I/O interface. */
4806 AssertBreakStmt(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
4807 rc = VERR_INVALID_PARAMETER);
4808 rc = VDInterfaceAdd(&pImage->VDIo.VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
4809 &pDisk->VDIIOIntCallbacks, &pImage->VDIo, &pImage->pVDIfsImage);
4810 AssertRC(rc);
4811
4812 /* Create UUID if the caller didn't specify one. */
4813 if (!pUuid)
4814 {
4815 rc = RTUuidCreate(&uuid);
4816 if (RT_FAILURE(rc))
4817 {
4818 rc = vdError(pDisk, rc, RT_SRC_POS,
4819 N_("VD: cannot generate UUID for image '%s'"),
4820 pszFilename);
4821 break;
4822 }
4823 pUuid = &uuid;
4824 }
4825
4826 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
4827 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
4828 rc = pImage->Backend->pfnCreate(pImage->pszFilename, pDisk->cbSize,
4829 uImageFlags | VD_IMAGE_FLAGS_DIFF,
4830 pszComment, &pDisk->PCHSGeometry,
4831 &pDisk->LCHSGeometry, pUuid,
4832 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
4833 0, 99,
4834 pDisk->pVDIfsDisk,
4835 pImage->pVDIfsImage,
4836 pVDIfsOperation,
4837 &pImage->pBackendData);
4838
4839 if (RT_SUCCESS(rc))
4840 {
4841 pImage->VDIo.pBackendData = pImage->pBackendData;
4842 pImage->uImageFlags = uImageFlags;
4843
4844 /* Lock disk for writing, as we modify pDisk information below. */
4845 rc2 = vdThreadStartWrite(pDisk);
4846 AssertRC(rc2);
4847 fLockWrite = true;
4848
4849 /* Switch previous image to read-only mode. */
4850 unsigned uOpenFlagsPrevImg;
4851 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
4852 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
4853 {
4854 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
4855 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pBackendData, uOpenFlagsPrevImg);
4856 }
4857
4858 /** @todo optionally check UUIDs */
4859
4860 /* Re-check state, as the lock wasn't held and another image
4861 * creation call could have been done by another thread. */
4862 AssertMsgStmt(pDisk->cImages != 0,
4863 ("Create diff image cannot be done without other images open\n"),
4864 rc = VERR_VD_INVALID_STATE);
4865 }
4866
4867 if (RT_SUCCESS(rc))
4868 {
4869 RTUUID Uuid;
4870 RTTIMESPEC ts;
4871
4872 if (pParentUuid && !RTUuidIsNull(pParentUuid))
4873 {
4874 Uuid = *pParentUuid;
4875 pImage->Backend->pfnSetParentUuid(pImage->pBackendData, &Uuid);
4876 }
4877 else
4878 {
4879 rc2 = pDisk->pLast->Backend->pfnGetUuid(pDisk->pLast->pBackendData,
4880 &Uuid);
4881 if (RT_SUCCESS(rc2))
4882 pImage->Backend->pfnSetParentUuid(pImage->pBackendData, &Uuid);
4883 }
4884 rc2 = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
4885 &Uuid);
4886 if (RT_SUCCESS(rc2))
4887 pImage->Backend->pfnSetParentModificationUuid(pImage->pBackendData,
4888 &Uuid);
4889 if (pDisk->pLast->Backend->pfnGetTimeStamp)
4890 rc2 = pDisk->pLast->Backend->pfnGetTimeStamp(pDisk->pLast->pBackendData,
4891 &ts);
4892 else
4893 rc2 = VERR_NOT_IMPLEMENTED;
4894 if (RT_SUCCESS(rc2) && pImage->Backend->pfnSetParentTimeStamp)
4895 pImage->Backend->pfnSetParentTimeStamp(pImage->pBackendData, &ts);
4896
4897 if (pImage->Backend->pfnSetParentFilename)
4898 rc2 = pImage->Backend->pfnSetParentFilename(pImage->pBackendData, pDisk->pLast->pszFilename);
4899 }
4900
4901 if (RT_SUCCESS(rc))
4902 {
4903 /* Image successfully opened, make it the last image. */
4904 vdAddImageToList(pDisk, pImage);
4905 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
4906 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
4907 }
4908 else
4909 {
4910 /* Error detected, but image opened. Close and delete image. */
4911 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, true);
4912 AssertRC(rc2);
4913 pImage->pBackendData = NULL;
4914 }
4915 } while (0);
4916
4917 if (RT_UNLIKELY(fLockWrite))
4918 {
4919 rc2 = vdThreadFinishWrite(pDisk);
4920 AssertRC(rc2);
4921 }
4922 else if (RT_UNLIKELY(fLockRead))
4923 {
4924 rc2 = vdThreadFinishRead(pDisk);
4925 AssertRC(rc2);
4926 }
4927
4928 if (RT_FAILURE(rc))
4929 {
4930 if (pImage)
4931 {
4932 if (pImage->pszFilename)
4933 RTStrFree(pImage->pszFilename);
4934 RTMemFree(pImage);
4935 }
4936 }
4937
4938 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
4939 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
4940
4941 LogFlowFunc(("returns %Rrc\n", rc));
4942 return rc;
4943}
4944
4945
4946/**
4947 * Creates and opens new cache image file in HDD container.
4948 *
4949 * @return VBox status code.
4950 * @param pDisk Name of the cache file backend to use (case insensitive).
4951 * @param pszFilename Name of the differencing cache file to create.
4952 * @param cbSize Maximum size of the cache.
4953 * @param uImageFlags Flags specifying special cache features.
4954 * @param pszComment Pointer to image comment. NULL is ok.
4955 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
4956 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
4957 * @param pVDIfsCache Pointer to the per-cache VD interface list.
4958 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
4959 */
4960VBOXDDU_DECL(int) VDCreateCache(PVBOXHDD pDisk, const char *pszBackend,
4961 const char *pszFilename, uint64_t cbSize,
4962 unsigned uImageFlags, const char *pszComment,
4963 PCRTUUID pUuid, unsigned uOpenFlags,
4964 PVDINTERFACE pVDIfsCache, PVDINTERFACE pVDIfsOperation)
4965{
4966 int rc = VINF_SUCCESS;
4967 int rc2;
4968 bool fLockWrite = false, fLockRead = false;
4969 PVDCACHE pCache = NULL;
4970 RTUUID uuid;
4971
4972 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
4973 pDisk, pszBackend, pszFilename, cbSize, uImageFlags, pszComment, pUuid, uOpenFlags, pVDIfsCache, pVDIfsOperation));
4974
4975 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
4976 VDINTERFACETYPE_PROGRESS);
4977 PVDINTERFACEPROGRESS pCbProgress = NULL;
4978 if (pIfProgress)
4979 pCbProgress = VDGetInterfaceProgress(pIfProgress);
4980
4981 do
4982 {
4983 /* sanity check */
4984 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4985 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4986
4987 /* Check arguments. */
4988 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
4989 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
4990 rc = VERR_INVALID_PARAMETER);
4991 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
4992 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
4993 rc = VERR_INVALID_PARAMETER);
4994 AssertMsgBreakStmt(cbSize,
4995 ("cbSize=%llu\n", cbSize),
4996 rc = VERR_INVALID_PARAMETER);
4997 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
4998 ("uImageFlags=%#x\n", uImageFlags),
4999 rc = VERR_INVALID_PARAMETER);
5000 /* The UUID may be NULL. */
5001 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
5002 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
5003 rc = VERR_INVALID_PARAMETER);
5004 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
5005 ("uOpenFlags=%#x\n", uOpenFlags),
5006 rc = VERR_INVALID_PARAMETER);
5007
5008 /* Check state. Needs a temporary read lock. Holding the write lock
5009 * all the time would be blocking other activities for too long. */
5010 rc2 = vdThreadStartRead(pDisk);
5011 AssertRC(rc2);
5012 fLockRead = true;
5013 AssertMsgBreakStmt(!pDisk->pCache,
5014 ("Create cache image cannot be done with a cache already attached\n"),
5015 rc = VERR_VD_CACHE_ALREADY_EXISTS);
5016 rc2 = vdThreadFinishRead(pDisk);
5017 AssertRC(rc2);
5018 fLockRead = false;
5019
5020 /* Set up image descriptor. */
5021 pCache = (PVDCACHE)RTMemAllocZ(sizeof(VDCACHE));
5022 if (!pCache)
5023 {
5024 rc = VERR_NO_MEMORY;
5025 break;
5026 }
5027 pCache->pszFilename = RTStrDup(pszFilename);
5028 if (!pCache->pszFilename)
5029 {
5030 rc = VERR_NO_MEMORY;
5031 break;
5032 }
5033
5034 rc = vdFindCacheBackend(pszBackend, &pCache->Backend);
5035 if (RT_FAILURE(rc))
5036 break;
5037 if (!pCache->Backend)
5038 {
5039 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
5040 N_("VD: unknown backend name '%s'"), pszBackend);
5041 break;
5042 }
5043
5044 pCache->VDIo.pDisk = pDisk;
5045 pCache->pVDIfsCache = pVDIfsCache;
5046
5047 /* Set up the I/O interface. */
5048 pCache->VDIo.pInterfaceIO = VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IO);
5049 if (pCache->VDIo.pInterfaceIO)
5050 pCache->VDIo.pInterfaceIOCallbacks = VDGetInterfaceIO(pCache->VDIo.pInterfaceIO);
5051 else
5052 {
5053 rc = VDInterfaceAdd(&pCache->VDIo.VDIIO, "VD_IO", VDINTERFACETYPE_IO,
5054 &pDisk->VDIIOCallbacks, pDisk, &pVDIfsCache);
5055 pCache->VDIo.pInterfaceIO = &pCache->VDIo.VDIIO;
5056 pCache->VDIo.pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
5057 }
5058
5059 /* Set up the internal I/O interface. */
5060 AssertBreakStmt(!VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IOINT),
5061 rc = VERR_INVALID_PARAMETER);
5062 rc = VDInterfaceAdd(&pCache->VDIo.VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
5063 &pDisk->VDIIOIntCallbacks, &pCache->VDIo, &pCache->pVDIfsCache);
5064 AssertRC(rc);
5065
5066 /* Create UUID if the caller didn't specify one. */
5067 if (!pUuid)
5068 {
5069 rc = RTUuidCreate(&uuid);
5070 if (RT_FAILURE(rc))
5071 {
5072 rc = vdError(pDisk, rc, RT_SRC_POS,
5073 N_("VD: cannot generate UUID for image '%s'"),
5074 pszFilename);
5075 break;
5076 }
5077 pUuid = &uuid;
5078 }
5079
5080 pCache->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
5081 rc = pCache->Backend->pfnCreate(pCache->pszFilename, cbSize,
5082 uImageFlags,
5083 pszComment, pUuid,
5084 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
5085 0, 99,
5086 pDisk->pVDIfsDisk,
5087 pCache->pVDIfsCache,
5088 pVDIfsOperation,
5089 &pCache->pBackendData);
5090
5091 if (RT_SUCCESS(rc))
5092 {
5093 /* Lock disk for writing, as we modify pDisk information below. */
5094 rc2 = vdThreadStartWrite(pDisk);
5095 AssertRC(rc2);
5096 fLockWrite = true;
5097
5098 pCache->VDIo.pBackendData = pCache->pBackendData;
5099
5100 /* Re-check state, as the lock wasn't held and another image
5101 * creation call could have been done by another thread. */
5102 AssertMsgStmt(!pDisk->pCache,
5103 ("Create cache image cannot be done with another cache open\n"),
5104 rc = VERR_VD_CACHE_ALREADY_EXISTS);
5105 }
5106
5107 if ( RT_SUCCESS(rc)
5108 && pDisk->pLast)
5109 {
5110 RTUUID UuidModification;
5111
5112 /* Set same modification Uuid as the last image. */
5113 rc = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
5114 &UuidModification);
5115 if (RT_SUCCESS(rc))
5116 {
5117 rc = pCache->Backend->pfnSetModificationUuid(pCache->pBackendData,
5118 &UuidModification);
5119 }
5120
5121 if (rc == VERR_NOT_SUPPORTED)
5122 rc = VINF_SUCCESS;
5123 }
5124
5125 if (RT_SUCCESS(rc))
5126 {
5127 /* Cache successfully created. */
5128 pDisk->pCache = pCache;
5129 }
5130 else
5131 {
5132 /* Error detected, but image opened. Close and delete image. */
5133 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, true);
5134 AssertRC(rc2);
5135 pCache->pBackendData = NULL;
5136 }
5137 } while (0);
5138
5139 if (RT_UNLIKELY(fLockWrite))
5140 {
5141 rc2 = vdThreadFinishWrite(pDisk);
5142 AssertRC(rc2);
5143 }
5144 else if (RT_UNLIKELY(fLockRead))
5145 {
5146 rc2 = vdThreadFinishRead(pDisk);
5147 AssertRC(rc2);
5148 }
5149
5150 if (RT_FAILURE(rc))
5151 {
5152 if (pCache)
5153 {
5154 if (pCache->pszFilename)
5155 RTStrFree(pCache->pszFilename);
5156 RTMemFree(pCache);
5157 }
5158 }
5159
5160 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
5161 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
5162
5163 LogFlowFunc(("returns %Rrc\n", rc));
5164 return rc;
5165}
5166
5167/**
5168 * Merges two images (not necessarily with direct parent/child relationship).
5169 * As a side effect the source image and potentially the other images which
5170 * are also merged to the destination are deleted from both the disk and the
5171 * images in the HDD container.
5172 *
5173 * @returns VBox status code.
5174 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
5175 * @param pDisk Pointer to HDD container.
5176 * @param nImageFrom Name of the image file to merge from.
5177 * @param nImageTo Name of the image file to merge to.
5178 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
5179 */
5180VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
5181 unsigned nImageTo, PVDINTERFACE pVDIfsOperation)
5182{
5183 int rc = VINF_SUCCESS;
5184 int rc2;
5185 bool fLockWrite = false, fLockRead = false;
5186 void *pvBuf = NULL;
5187
5188 LogFlowFunc(("pDisk=%#p nImageFrom=%u nImageTo=%u pVDIfsOperation=%#p\n",
5189 pDisk, nImageFrom, nImageTo, pVDIfsOperation));
5190
5191 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
5192 VDINTERFACETYPE_PROGRESS);
5193 PVDINTERFACEPROGRESS pCbProgress = NULL;
5194 if (pIfProgress)
5195 pCbProgress = VDGetInterfaceProgress(pIfProgress);
5196
5197 do
5198 {
5199 /* sanity check */
5200 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
5201 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5202
5203 /* For simplicity reasons lock for writing as the image reopen below
5204 * might need it. After all the reopen is usually needed. */
5205 rc2 = vdThreadStartWrite(pDisk);
5206 AssertRC(rc2);
5207 fLockWrite = true;
5208 PVDIMAGE pImageFrom = vdGetImageByNumber(pDisk, nImageFrom);
5209 PVDIMAGE pImageTo = vdGetImageByNumber(pDisk, nImageTo);
5210 if (!pImageFrom || !pImageTo)
5211 {
5212 rc = VERR_VD_IMAGE_NOT_FOUND;
5213 break;
5214 }
5215 AssertBreakStmt(pImageFrom != pImageTo, rc = VERR_INVALID_PARAMETER);
5216
5217 /* Make sure destination image is writable. */
5218 unsigned uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pBackendData);
5219 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
5220 {
5221 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
5222 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
5223 uOpenFlags);
5224 if (RT_FAILURE(rc))
5225 break;
5226 }
5227
5228 /* Get size of destination image. */
5229 uint64_t cbSize = pImageTo->Backend->pfnGetSize(pImageTo->pBackendData);
5230 rc2 = vdThreadFinishWrite(pDisk);
5231 AssertRC(rc2);
5232 fLockWrite = false;
5233
5234 /* Allocate tmp buffer. */
5235 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
5236 if (!pvBuf)
5237 {
5238 rc = VERR_NO_MEMORY;
5239 break;
5240 }
5241
5242 /* Merging is done directly on the images itself. This potentially
5243 * causes trouble if the disk is full in the middle of operation. */
5244 if (nImageFrom < nImageTo)
5245 {
5246 /* Merge parent state into child. This means writing all not
5247 * allocated blocks in the destination image which are allocated in
5248 * the images to be merged. */
5249 uint64_t uOffset = 0;
5250 uint64_t cbRemaining = cbSize;
5251 do
5252 {
5253 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
5254
5255 /* Need to hold the write lock during a read-write operation. */
5256 rc2 = vdThreadStartWrite(pDisk);
5257 AssertRC(rc2);
5258 fLockWrite = true;
5259
5260 rc = pImageTo->Backend->pfnRead(pImageTo->pBackendData,
5261 uOffset, pvBuf, cbThisRead,
5262 &cbThisRead);
5263 if (rc == VERR_VD_BLOCK_FREE)
5264 {
5265 /* Search for image with allocated block. Do not attempt to
5266 * read more than the previous reads marked as valid.
5267 * Otherwise this would return stale data when different
5268 * block sizes are used for the images. */
5269 for (PVDIMAGE pCurrImage = pImageTo->pPrev;
5270 pCurrImage != NULL && pCurrImage != pImageFrom->pPrev && rc == VERR_VD_BLOCK_FREE;
5271 pCurrImage = pCurrImage->pPrev)
5272 {
5273 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
5274 uOffset, pvBuf,
5275 cbThisRead,
5276 &cbThisRead);
5277 }
5278
5279 if (rc != VERR_VD_BLOCK_FREE)
5280 {
5281 if (RT_FAILURE(rc))
5282 break;
5283 /* Updating the cache is required because this might be a live merge. */
5284 rc = vdWriteHelper(pDisk, pImageTo, pImageFrom->pPrev,
5285 uOffset, pvBuf, cbThisRead,
5286 true /* fUpdateCache */);
5287 if (RT_FAILURE(rc))
5288 break;
5289 }
5290 else
5291 rc = VINF_SUCCESS;
5292 }
5293 else if (RT_FAILURE(rc))
5294 break;
5295
5296 rc2 = vdThreadFinishWrite(pDisk);
5297 AssertRC(rc2);
5298 fLockWrite = false;
5299
5300 uOffset += cbThisRead;
5301 cbRemaining -= cbThisRead;
5302
5303 if (pCbProgress && pCbProgress->pfnProgress)
5304 {
5305 /** @todo r=klaus: this can update the progress to the same
5306 * percentage over and over again if the image format makes
5307 * relatively small increments. */
5308 rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
5309 uOffset * 99 / cbSize);
5310 if (RT_FAILURE(rc))
5311 break;
5312 }
5313 } while (uOffset < cbSize);
5314 }
5315 else
5316 {
5317 /*
5318 * We may need to update the parent uuid of the child coming after
5319 * the last image to be merged. We have to reopen it read/write.
5320 *
5321 * This is done before we do the actual merge to prevent an
5322 * inconsistent chain if the mode change fails for some reason.
5323 */
5324 if (pImageFrom->pNext)
5325 {
5326 PVDIMAGE pImageChild = pImageFrom->pNext;
5327
5328 /* Take the write lock. */
5329 rc2 = vdThreadStartWrite(pDisk);
5330 AssertRC(rc2);
5331 fLockWrite = true;
5332
5333 /* We need to open the image in read/write mode. */
5334 uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pBackendData);
5335
5336 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
5337 {
5338 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
5339 rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pBackendData,
5340 uOpenFlags);
5341 if (RT_FAILURE(rc))
5342 break;
5343 }
5344
5345 rc2 = vdThreadFinishWrite(pDisk);
5346 AssertRC(rc2);
5347 fLockWrite = false;
5348 }
5349
5350 /* If the merge is from the last image we have to relay all writes
5351 * to the merge destination as well, so that concurrent writes
5352 * (in case of a live merge) are handled correctly. */
5353 if (!pImageFrom->pNext)
5354 {
5355 /* Take the write lock. */
5356 rc2 = vdThreadStartWrite(pDisk);
5357 AssertRC(rc2);
5358 fLockWrite = true;
5359
5360 pDisk->pImageRelay = pImageTo;
5361
5362 rc2 = vdThreadFinishWrite(pDisk);
5363 AssertRC(rc2);
5364 fLockWrite = false;
5365 }
5366
5367 /* Merge child state into parent. This means writing all blocks
5368 * which are allocated in the image up to the source image to the
5369 * destination image. */
5370 uint64_t uOffset = 0;
5371 uint64_t cbRemaining = cbSize;
5372 do
5373 {
5374 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
5375 rc = VERR_VD_BLOCK_FREE;
5376
5377 /* Need to hold the write lock during a read-write operation. */
5378 rc2 = vdThreadStartWrite(pDisk);
5379 AssertRC(rc2);
5380 fLockWrite = true;
5381
5382 /* Search for image with allocated block. Do not attempt to
5383 * read more than the previous reads marked as valid. Otherwise
5384 * this would return stale data when different block sizes are
5385 * used for the images. */
5386 for (PVDIMAGE pCurrImage = pImageFrom;
5387 pCurrImage != NULL && pCurrImage != pImageTo && rc == VERR_VD_BLOCK_FREE;
5388 pCurrImage = pCurrImage->pPrev)
5389 {
5390 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
5391 uOffset, pvBuf,
5392 cbThisRead, &cbThisRead);
5393 }
5394
5395 if (rc != VERR_VD_BLOCK_FREE)
5396 {
5397 if (RT_FAILURE(rc))
5398 break;
5399 rc = vdWriteHelper(pDisk, pImageTo, NULL, uOffset, pvBuf,
5400 cbThisRead, true /* fUpdateCache */);
5401 if (RT_FAILURE(rc))
5402 break;
5403 }
5404 else
5405 rc = VINF_SUCCESS;
5406
5407 rc2 = vdThreadFinishWrite(pDisk);
5408 AssertRC(rc2);
5409 fLockWrite = false;
5410
5411 uOffset += cbThisRead;
5412 cbRemaining -= cbThisRead;
5413
5414 if (pCbProgress && pCbProgress->pfnProgress)
5415 {
5416 /** @todo r=klaus: this can update the progress to the same
5417 * percentage over and over again if the image format makes
5418 * relatively small increments. */
5419 rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
5420 uOffset * 99 / cbSize);
5421 if (RT_FAILURE(rc))
5422 break;
5423 }
5424 } while (uOffset < cbSize);
5425
5426 /* In case we set up a "write proxy" image above we must clear
5427 * this again now to prevent stray writes. Failure or not. */
5428 if (!pImageFrom->pNext)
5429 {
5430 /* Take the write lock. */
5431 rc2 = vdThreadStartWrite(pDisk);
5432 AssertRC(rc2);
5433 fLockWrite = true;
5434
5435 pDisk->pImageRelay = NULL;
5436
5437 rc2 = vdThreadFinishWrite(pDisk);
5438 AssertRC(rc2);
5439 fLockWrite = false;
5440 }
5441 }
5442
5443 /*
5444 * Leave in case of an error to avoid corrupted data in the image chain
5445 * (includes cancelling the operation by the user).
5446 */
5447 if (RT_FAILURE(rc))
5448 break;
5449
5450 /* Need to hold the write lock while finishing the merge. */
5451 rc2 = vdThreadStartWrite(pDisk);
5452 AssertRC(rc2);
5453 fLockWrite = true;
5454
5455 /* Update parent UUID so that image chain is consistent. */
5456 RTUUID Uuid;
5457 PVDIMAGE pImageChild = NULL;
5458 if (nImageFrom < nImageTo)
5459 {
5460 if (pImageFrom->pPrev)
5461 {
5462 rc = pImageFrom->pPrev->Backend->pfnGetUuid(pImageFrom->pPrev->pBackendData,
5463 &Uuid);
5464 AssertRC(rc);
5465 }
5466 else
5467 RTUuidClear(&Uuid);
5468 rc = pImageTo->Backend->pfnSetParentUuid(pImageTo->pBackendData,
5469 &Uuid);
5470 AssertRC(rc);
5471 }
5472 else
5473 {
5474 /* Update the parent uuid of the child of the last merged image. */
5475 if (pImageFrom->pNext)
5476 {
5477 rc = pImageTo->Backend->pfnGetUuid(pImageTo->pBackendData,
5478 &Uuid);
5479 AssertRC(rc);
5480
5481 rc = pImageFrom->Backend->pfnSetParentUuid(pImageFrom->pNext->pBackendData,
5482 &Uuid);
5483 AssertRC(rc);
5484
5485 pImageChild = pImageFrom->pNext;
5486 }
5487 }
5488
5489 /* Delete the no longer needed images. */
5490 PVDIMAGE pImg = pImageFrom, pTmp;
5491 while (pImg != pImageTo)
5492 {
5493 if (nImageFrom < nImageTo)
5494 pTmp = pImg->pNext;
5495 else
5496 pTmp = pImg->pPrev;
5497 vdRemoveImageFromList(pDisk, pImg);
5498 pImg->Backend->pfnClose(pImg->pBackendData, true);
5499 RTMemFree(pImg->pszFilename);
5500 RTMemFree(pImg);
5501 pImg = pTmp;
5502 }
5503
5504 /* Make sure destination image is back to read only if necessary. */
5505 if (pImageTo != pDisk->pLast)
5506 {
5507 uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pBackendData);
5508 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
5509 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
5510 uOpenFlags);
5511 if (RT_FAILURE(rc))
5512 break;
5513 }
5514
5515 /*
5516 * Make sure the child is readonly
5517 * for the child -> parent merge direction
5518 * if necessary.
5519 */
5520 if ( nImageFrom > nImageTo
5521 && pImageChild
5522 && pImageChild != pDisk->pLast)
5523 {
5524 uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pBackendData);
5525 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
5526 rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pBackendData,
5527 uOpenFlags);
5528 if (RT_FAILURE(rc))
5529 break;
5530 }
5531 } while (0);
5532
5533 if (RT_UNLIKELY(fLockWrite))
5534 {
5535 rc2 = vdThreadFinishWrite(pDisk);
5536 AssertRC(rc2);
5537 }
5538 else if (RT_UNLIKELY(fLockRead))
5539 {
5540 rc2 = vdThreadFinishRead(pDisk);
5541 AssertRC(rc2);
5542 }
5543
5544 if (pvBuf)
5545 RTMemTmpFree(pvBuf);
5546
5547 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
5548 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
5549
5550 LogFlowFunc(("returns %Rrc\n", rc));
5551 return rc;
5552}
5553
5554/**
5555 * Copies an image from one HDD container to another.
5556 * The copy is opened in the target HDD container.
5557 * It is possible to convert between different image formats, because the
5558 * backend for the destination may be different from the source.
5559 * If both the source and destination reference the same HDD container,
5560 * then the image is moved (by copying/deleting or renaming) to the new location.
5561 * The source container is unchanged if the move operation fails, otherwise
5562 * the image at the new location is opened in the same way as the old one was.
5563 *
5564 * @returns VBox status code.
5565 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
5566 * @param pDiskFrom Pointer to source HDD container.
5567 * @param nImage Image number, counts from 0. 0 is always base image of container.
5568 * @param pDiskTo Pointer to destination HDD container.
5569 * @param pszBackend Name of the image file backend to use.
5570 * @param pszFilename New name of the image (may be NULL if pDiskFrom == pDiskTo).
5571 * @param fMoveByRename If true, attempt to perform a move by renaming (if successful the new size is ignored).
5572 * @param cbSize New image size (0 means leave unchanged).
5573 * @param uImageFlags Flags specifying special destination image features.
5574 * @param pDstUuid New UUID of the destination image. If NULL, a new UUID is created.
5575 * This parameter is used if and only if a true copy is created.
5576 * In all rename/move cases the UUIDs are copied over.
5577 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
5578 * Only used if the destination image is created.
5579 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
5580 * @param pDstVDIfsImage Pointer to the per-image VD interface list, for the
5581 * destination image.
5582 * @param pDstVDIfsOperation Pointer to the per-image VD interface list,
5583 * for the destination image.
5584 */
5585VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
5586 const char *pszBackend, const char *pszFilename,
5587 bool fMoveByRename, uint64_t cbSize,
5588 unsigned uImageFlags, PCRTUUID pDstUuid,
5589 unsigned uOpenFlags, PVDINTERFACE pVDIfsOperation,
5590 PVDINTERFACE pDstVDIfsImage,
5591 PVDINTERFACE pDstVDIfsOperation)
5592{
5593 int rc = VINF_SUCCESS;
5594 int rc2;
5595 bool fLockReadFrom = false, fLockWriteFrom = false, fLockWriteTo = false;
5596 void *pvBuf = NULL;
5597 PVDIMAGE pImageTo = NULL;
5598
5599 LogFlowFunc(("pDiskFrom=%#p nImage=%u pDiskTo=%#p pszBackend=\"%s\" pszFilename=\"%s\" fMoveByRename=%d cbSize=%llu uImageFlags=%#x pDstUuid=%#p uOpenFlags=%#x pVDIfsOperation=%#p pDstVDIfsImage=%#p pDstVDIfsOperation=%#p\n",
5600 pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename, cbSize, uImageFlags, pDstUuid, uOpenFlags, pVDIfsOperation, pDstVDIfsImage, pDstVDIfsOperation));
5601
5602 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
5603 VDINTERFACETYPE_PROGRESS);
5604 PVDINTERFACEPROGRESS pCbProgress = NULL;
5605 if (pIfProgress)
5606 pCbProgress = VDGetInterfaceProgress(pIfProgress);
5607
5608 PVDINTERFACE pDstIfProgress = VDInterfaceGet(pDstVDIfsOperation,
5609 VDINTERFACETYPE_PROGRESS);
5610 PVDINTERFACEPROGRESS pDstCbProgress = NULL;
5611 if (pDstIfProgress)
5612 pDstCbProgress = VDGetInterfaceProgress(pDstIfProgress);
5613
5614 do {
5615 /* Check arguments. */
5616 AssertMsgBreakStmt(VALID_PTR(pDiskFrom), ("pDiskFrom=%#p\n", pDiskFrom),
5617 rc = VERR_INVALID_PARAMETER);
5618 AssertMsg(pDiskFrom->u32Signature == VBOXHDDDISK_SIGNATURE,
5619 ("u32Signature=%08x\n", pDiskFrom->u32Signature));
5620
5621 rc2 = vdThreadStartRead(pDiskFrom);
5622 AssertRC(rc2);
5623 fLockReadFrom = true;
5624 PVDIMAGE pImageFrom = vdGetImageByNumber(pDiskFrom, nImage);
5625 AssertPtrBreakStmt(pImageFrom, rc = VERR_VD_IMAGE_NOT_FOUND);
5626 AssertMsgBreakStmt(VALID_PTR(pDiskTo), ("pDiskTo=%#p\n", pDiskTo),
5627 rc = VERR_INVALID_PARAMETER);
5628 AssertMsg(pDiskTo->u32Signature == VBOXHDDDISK_SIGNATURE,
5629 ("u32Signature=%08x\n", pDiskTo->u32Signature));
5630
5631 /* Move the image. */
5632 if (pDiskFrom == pDiskTo)
5633 {
5634 /* Rename only works when backends are the same, are file based
5635 * and the rename method is implemented. */
5636 if ( fMoveByRename
5637 && !RTStrICmp(pszBackend, pImageFrom->Backend->pszBackendName)
5638 && pImageFrom->Backend->uBackendCaps & VD_CAP_FILE
5639 && pImageFrom->Backend->pfnRename)
5640 {
5641 rc2 = vdThreadFinishRead(pDiskFrom);
5642 AssertRC(rc2);
5643 fLockReadFrom = false;
5644
5645 rc2 = vdThreadStartWrite(pDiskFrom);
5646 AssertRC(rc2);
5647 fLockWriteFrom = true;
5648 rc = pImageFrom->Backend->pfnRename(pImageFrom->pBackendData, pszFilename ? pszFilename : pImageFrom->pszFilename);
5649 break;
5650 }
5651
5652 /** @todo Moving (including shrinking/growing) of the image is
5653 * requested, but the rename attempt failed or it wasn't possible.
5654 * Must now copy image to temp location. */
5655 AssertReleaseMsgFailed(("VDCopy: moving by copy/delete not implemented\n"));
5656 }
5657
5658 /* pszFilename is allowed to be NULL, as this indicates copy to the existing image. */
5659 AssertMsgBreakStmt(pszFilename == NULL || (VALID_PTR(pszFilename) && *pszFilename),
5660 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
5661 rc = VERR_INVALID_PARAMETER);
5662
5663 uint64_t cbSizeFrom;
5664 cbSizeFrom = pImageFrom->Backend->pfnGetSize(pImageFrom->pBackendData);
5665 if (cbSizeFrom == 0)
5666 {
5667 rc = VERR_VD_VALUE_NOT_FOUND;
5668 break;
5669 }
5670
5671 VDGEOMETRY PCHSGeometryFrom = {0, 0, 0};
5672 VDGEOMETRY LCHSGeometryFrom = {0, 0, 0};
5673 pImageFrom->Backend->pfnGetPCHSGeometry(pImageFrom->pBackendData, &PCHSGeometryFrom);
5674 pImageFrom->Backend->pfnGetLCHSGeometry(pImageFrom->pBackendData, &LCHSGeometryFrom);
5675
5676 RTUUID ImageUuid, ImageModificationUuid;
5677 if (pDiskFrom != pDiskTo)
5678 {
5679 if (pDstUuid)
5680 ImageUuid = *pDstUuid;
5681 else
5682 RTUuidCreate(&ImageUuid);
5683 }
5684 else
5685 {
5686 rc = pImageFrom->Backend->pfnGetUuid(pImageFrom->pBackendData, &ImageUuid);
5687 if (RT_FAILURE(rc))
5688 RTUuidCreate(&ImageUuid);
5689 }
5690 rc = pImageFrom->Backend->pfnGetModificationUuid(pImageFrom->pBackendData, &ImageModificationUuid);
5691 if (RT_FAILURE(rc))
5692 RTUuidClear(&ImageModificationUuid);
5693
5694 char szComment[1024];
5695 rc = pImageFrom->Backend->pfnGetComment(pImageFrom->pBackendData, szComment, sizeof(szComment));
5696 if (RT_FAILURE(rc))
5697 szComment[0] = '\0';
5698 else
5699 szComment[sizeof(szComment) - 1] = '\0';
5700
5701 rc2 = vdThreadFinishRead(pDiskFrom);
5702 AssertRC(rc2);
5703 fLockReadFrom = false;
5704
5705 rc2 = vdThreadStartRead(pDiskTo);
5706 AssertRC(rc2);
5707 unsigned cImagesTo = pDiskTo->cImages;
5708 rc2 = vdThreadFinishRead(pDiskTo);
5709 AssertRC(rc2);
5710
5711 if (pszFilename)
5712 {
5713 if (cbSize == 0)
5714 cbSize = cbSizeFrom;
5715
5716 /* Create destination image with the properties of source image. */
5717 /** @todo replace the VDCreateDiff/VDCreateBase calls by direct
5718 * calls to the backend. Unifies the code and reduces the API
5719 * dependencies. Would also make the synchronization explicit. */
5720 if (cImagesTo > 0)
5721 {
5722 rc = VDCreateDiff(pDiskTo, pszBackend, pszFilename,
5723 uImageFlags, szComment, &ImageUuid,
5724 NULL /* pParentUuid */,
5725 uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
5726 pDstVDIfsImage, NULL);
5727
5728 rc2 = vdThreadStartWrite(pDiskTo);
5729 AssertRC(rc2);
5730 fLockWriteTo = true;
5731 } else {
5732 /** @todo hack to force creation of a fixed image for
5733 * the RAW backend, which can't handle anything else. */
5734 if (!RTStrICmp(pszBackend, "RAW"))
5735 uImageFlags |= VD_IMAGE_FLAGS_FIXED;
5736
5737 /* Fix broken PCHS geometry. Can happen for two reasons: either
5738 * the backend mixes up PCHS and LCHS, or the application used
5739 * to create the source image has put garbage in it. */
5740 /** @todo double-check if the VHD backend correctly handles
5741 * PCHS and LCHS geometry. also reconsider our current paranoia
5742 * level when it comes to geometry settings here and in the
5743 * backends. */
5744 if (PCHSGeometryFrom.cHeads > 16 || PCHSGeometryFrom.cSectors > 63)
5745 {
5746 Assert(RT_MIN(cbSize / 512 / 16 / 63, 16383) - (uint32_t)RT_MIN(cbSize / 512 / 16 / 63, 16383));
5747 PCHSGeometryFrom.cCylinders = (uint32_t)RT_MIN(cbSize / 512 / 16 / 63, 16383);
5748 PCHSGeometryFrom.cHeads = 16;
5749 PCHSGeometryFrom.cSectors = 63;
5750 }
5751
5752 rc = VDCreateBase(pDiskTo, pszBackend, pszFilename, cbSize,
5753 uImageFlags, szComment,
5754 &PCHSGeometryFrom, &LCHSGeometryFrom,
5755 NULL, uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
5756 pDstVDIfsImage, NULL);
5757
5758 rc2 = vdThreadStartWrite(pDiskTo);
5759 AssertRC(rc2);
5760 fLockWriteTo = true;
5761
5762 if (RT_SUCCESS(rc) && !RTUuidIsNull(&ImageUuid))
5763 pDiskTo->pLast->Backend->pfnSetUuid(pDiskTo->pLast->pBackendData, &ImageUuid);
5764 }
5765 if (RT_FAILURE(rc))
5766 break;
5767
5768 pImageTo = pDiskTo->pLast;
5769 AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
5770
5771 cbSize = RT_MIN(cbSize, cbSizeFrom);
5772 }
5773 else
5774 {
5775 pImageTo = pDiskTo->pLast;
5776 AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
5777
5778 uint64_t cbSizeTo;
5779 cbSizeTo = pImageTo->Backend->pfnGetSize(pImageTo->pBackendData);
5780 if (cbSizeTo == 0)
5781 {
5782 rc = VERR_VD_VALUE_NOT_FOUND;
5783 break;
5784 }
5785
5786 if (cbSize == 0)
5787 cbSize = RT_MIN(cbSizeFrom, cbSizeTo);
5788 }
5789
5790 rc2 = vdThreadFinishWrite(pDiskTo);
5791 AssertRC(rc2);
5792 fLockWriteTo = false;
5793
5794 /* Allocate tmp buffer. */
5795 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
5796 if (!pvBuf)
5797 {
5798 rc = VERR_NO_MEMORY;
5799 break;
5800 }
5801
5802 /* Whether we can take the optimized copy path (false) or not.
5803 * Don't optimize if the image existed or if it is a child image. */
5804 bool fRegularRead = (pszFilename == NULL) || (cImagesTo > 0);
5805
5806 /* Copy the data. */
5807 uint64_t uOffset = 0;
5808 uint64_t cbRemaining = cbSize;
5809
5810 do
5811 {
5812 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
5813
5814 /* Note that we don't attempt to synchronize cross-disk accesses.
5815 * It wouldn't be very difficult to do, just the lock order would
5816 * need to be defined somehow to prevent deadlocks. Postpone such
5817 * magic as there is no use case for this. */
5818
5819 rc2 = vdThreadStartRead(pDiskFrom);
5820 AssertRC(rc2);
5821 fLockReadFrom = true;
5822
5823 /*
5824 * Updating the cache doesn't make any sense
5825 * as we are looping once through the image.
5826 */
5827 rc = vdReadHelper(pDiskFrom, pImageFrom, NULL, uOffset, pvBuf,
5828 cbThisRead, fRegularRead,
5829 false /* fUpdateCache */);
5830 if (RT_FAILURE(rc) && rc != VERR_VD_BLOCK_FREE)
5831 break;
5832
5833 rc2 = vdThreadFinishRead(pDiskFrom);
5834 AssertRC(rc2);
5835 fLockReadFrom = false;
5836
5837 if (rc != VERR_VD_BLOCK_FREE)
5838 {
5839 rc2 = vdThreadStartWrite(pDiskTo);
5840 AssertRC(rc2);
5841 fLockWriteTo = true;
5842
5843 rc = vdWriteHelper(pDiskTo, pImageTo, NULL, uOffset, pvBuf,
5844 cbThisRead, false /* fUpdateCache */);
5845 if (RT_FAILURE(rc))
5846 break;
5847
5848 rc2 = vdThreadFinishWrite(pDiskTo);
5849 AssertRC(rc2);
5850 fLockWriteTo = false;
5851 }
5852 else /* Don't propagate the error to the outside */
5853 rc = VINF_SUCCESS;
5854
5855 uOffset += cbThisRead;
5856 cbRemaining -= cbThisRead;
5857
5858 if (pCbProgress && pCbProgress->pfnProgress)
5859 {
5860 /** @todo r=klaus: this can update the progress to the same
5861 * percentage over and over again if the image format makes
5862 * relatively small increments. */
5863 rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
5864 uOffset * 99 / cbSize);
5865 if (RT_FAILURE(rc))
5866 break;
5867 }
5868 if (pDstCbProgress && pDstCbProgress->pfnProgress)
5869 {
5870 /** @todo r=klaus: this can update the progress to the same
5871 * percentage over and over again if the image format makes
5872 * relatively small increments. */
5873 rc = pDstCbProgress->pfnProgress(pDstIfProgress->pvUser,
5874 uOffset * 99 / cbSize);
5875 if (RT_FAILURE(rc))
5876 break;
5877 }
5878 } while (uOffset < cbSize);
5879
5880 if (RT_SUCCESS(rc))
5881 {
5882 rc2 = vdThreadStartWrite(pDiskTo);
5883 AssertRC(rc2);
5884 fLockWriteTo = true;
5885
5886 /* Only set modification UUID if it is non-null, since the source
5887 * backend might not provide a valid modification UUID. */
5888 if (!RTUuidIsNull(&ImageModificationUuid))
5889 pImageTo->Backend->pfnSetModificationUuid(pImageTo->pBackendData, &ImageModificationUuid);
5890
5891 /* Set the requested open flags if they differ from the value
5892 * required for creating the image and copying the contents. */
5893 if ( pImageTo && pszFilename
5894 && uOpenFlags != (uOpenFlags & ~VD_OPEN_FLAGS_READONLY))
5895 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
5896 uOpenFlags);
5897 }
5898 } while (0);
5899
5900 if (RT_FAILURE(rc) && pImageTo && pszFilename)
5901 {
5902 /* Take the write lock only if it is not taken. Not worth making the
5903 * above code even more complicated. */
5904 if (RT_UNLIKELY(!fLockWriteTo))
5905 {
5906 rc2 = vdThreadStartWrite(pDiskTo);
5907 AssertRC(rc2);
5908 fLockWriteTo = true;
5909 }
5910 /* Error detected, but new image created. Remove image from list. */
5911 vdRemoveImageFromList(pDiskTo, pImageTo);
5912
5913 /* Close and delete image. */
5914 rc2 = pImageTo->Backend->pfnClose(pImageTo->pBackendData, true);
5915 AssertRC(rc2);
5916 pImageTo->pBackendData = NULL;
5917
5918 /* Free remaining resources. */
5919 if (pImageTo->pszFilename)
5920 RTStrFree(pImageTo->pszFilename);
5921
5922 RTMemFree(pImageTo);
5923 }
5924
5925 if (RT_UNLIKELY(fLockWriteTo))
5926 {
5927 rc2 = vdThreadFinishWrite(pDiskTo);
5928 AssertRC(rc2);
5929 }
5930 if (RT_UNLIKELY(fLockWriteFrom))
5931 {
5932 rc2 = vdThreadFinishWrite(pDiskFrom);
5933 AssertRC(rc2);
5934 }
5935 else if (RT_UNLIKELY(fLockReadFrom))
5936 {
5937 rc2 = vdThreadFinishRead(pDiskFrom);
5938 AssertRC(rc2);
5939 }
5940
5941 if (pvBuf)
5942 RTMemTmpFree(pvBuf);
5943
5944 if (RT_SUCCESS(rc))
5945 {
5946 if (pCbProgress && pCbProgress->pfnProgress)
5947 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
5948 if (pDstCbProgress && pDstCbProgress->pfnProgress)
5949 pDstCbProgress->pfnProgress(pDstIfProgress->pvUser, 100);
5950 }
5951
5952 LogFlowFunc(("returns %Rrc\n", rc));
5953 return rc;
5954}
5955
5956/**
5957 * Optimizes the storage consumption of an image. Typically the unused blocks
5958 * have to be wiped with zeroes to achieve a substantial reduced storage use.
5959 * Another optimization done is reordering the image blocks, which can provide
5960 * a significant performance boost, as reads and writes tend to use less random
5961 * file offsets.
5962 *
5963 * @return VBox status code.
5964 * @return VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
5965 * @return VERR_VD_IMAGE_READ_ONLY if image is not writable.
5966 * @return VERR_NOT_SUPPORTED if this kind of image can be compacted, but
5967 * the code for this isn't implemented yet.
5968 * @param pDisk Pointer to HDD container.
5969 * @param nImage Image number, counts from 0. 0 is always base image of container.
5970 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
5971 */
5972VBOXDDU_DECL(int) VDCompact(PVBOXHDD pDisk, unsigned nImage,
5973 PVDINTERFACE pVDIfsOperation)
5974{
5975 int rc = VINF_SUCCESS;
5976 int rc2;
5977 bool fLockRead = false, fLockWrite = false;
5978 void *pvBuf = NULL;
5979 void *pvTmp = NULL;
5980
5981 LogFlowFunc(("pDisk=%#p nImage=%u pVDIfsOperation=%#p\n",
5982 pDisk, nImage, pVDIfsOperation));
5983
5984 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
5985 VDINTERFACETYPE_PROGRESS);
5986 PVDINTERFACEPROGRESS pCbProgress = NULL;
5987 if (pIfProgress)
5988 pCbProgress = VDGetInterfaceProgress(pIfProgress);
5989
5990 do {
5991 /* Check arguments. */
5992 AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
5993 rc = VERR_INVALID_PARAMETER);
5994 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE,
5995 ("u32Signature=%08x\n", pDisk->u32Signature));
5996
5997 rc2 = vdThreadStartRead(pDisk);
5998 AssertRC(rc2);
5999 fLockRead = true;
6000
6001 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6002 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
6003
6004 /* If there is no compact callback for not file based backends then
6005 * the backend doesn't need compaction. No need to make much fuss about
6006 * this. For file based ones signal this as not yet supported. */
6007 if (!pImage->Backend->pfnCompact)
6008 {
6009 if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
6010 rc = VERR_NOT_SUPPORTED;
6011 else
6012 rc = VINF_SUCCESS;
6013 break;
6014 }
6015
6016 /* Insert interface for reading parent state into per-operation list,
6017 * if there is a parent image. */
6018 VDINTERFACE IfOpParent;
6019 VDINTERFACEPARENTSTATE ParentCb;
6020 VDPARENTSTATEDESC ParentUser;
6021 if (pImage->pPrev)
6022 {
6023 ParentCb.cbSize = sizeof(ParentCb);
6024 ParentCb.enmInterface = VDINTERFACETYPE_PARENTSTATE;
6025 ParentCb.pfnParentRead = vdParentRead;
6026 ParentUser.pDisk = pDisk;
6027 ParentUser.pImage = pImage->pPrev;
6028 rc = VDInterfaceAdd(&IfOpParent, "VDCompact_ParentState", VDINTERFACETYPE_PARENTSTATE,
6029 &ParentCb, &ParentUser, &pVDIfsOperation);
6030 AssertRC(rc);
6031 }
6032
6033 rc2 = vdThreadFinishRead(pDisk);
6034 AssertRC(rc2);
6035 fLockRead = false;
6036
6037 rc2 = vdThreadStartWrite(pDisk);
6038 AssertRC(rc2);
6039 fLockWrite = true;
6040
6041 rc = pImage->Backend->pfnCompact(pImage->pBackendData,
6042 0, 99,
6043 pDisk->pVDIfsDisk,
6044 pImage->pVDIfsImage,
6045 pVDIfsOperation);
6046 } while (0);
6047
6048 if (RT_UNLIKELY(fLockWrite))
6049 {
6050 rc2 = vdThreadFinishWrite(pDisk);
6051 AssertRC(rc2);
6052 }
6053 else if (RT_UNLIKELY(fLockRead))
6054 {
6055 rc2 = vdThreadFinishRead(pDisk);
6056 AssertRC(rc2);
6057 }
6058
6059 if (pvBuf)
6060 RTMemTmpFree(pvBuf);
6061 if (pvTmp)
6062 RTMemTmpFree(pvTmp);
6063
6064 if (RT_SUCCESS(rc))
6065 {
6066 if (pCbProgress && pCbProgress->pfnProgress)
6067 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
6068 }
6069
6070 LogFlowFunc(("returns %Rrc\n", rc));
6071 return rc;
6072}
6073
6074/**
6075 * Resizes the the given disk image to the given size.
6076 *
6077 * @return VBox status
6078 * @return VERR_VD_IMAGE_READ_ONLY if image is not writable.
6079 * @return VERR_NOT_SUPPORTED if this kind of image can be compacted, but
6080 *
6081 * @param pDisk Pointer to the HDD container.
6082 * @param cbSize New size of the image.
6083 * @param pPCHSGeometry Pointer to the new physical disk geometry <= (16383,16,63). Not NULL.
6084 * @param pLCHSGeometry Pointer to the new logical disk geometry <= (x,255,63). Not NULL.
6085 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
6086 */
6087VBOXDDU_DECL(int) VDResize(PVBOXHDD pDisk, uint64_t cbSize,
6088 PCVDGEOMETRY pPCHSGeometry,
6089 PCVDGEOMETRY pLCHSGeometry,
6090 PVDINTERFACE pVDIfsOperation)
6091{
6092 /** @todo r=klaus resizing was designed to be part of VDCopy, so having a separate function is not desirable. */
6093 int rc = VINF_SUCCESS;
6094 int rc2;
6095 bool fLockRead = false, fLockWrite = false;
6096
6097 LogFlowFunc(("pDisk=%#p cbSize=%llu pVDIfsOperation=%#p\n",
6098 pDisk, cbSize, pVDIfsOperation));
6099
6100 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
6101 VDINTERFACETYPE_PROGRESS);
6102 PVDINTERFACEPROGRESS pCbProgress = NULL;
6103 if (pIfProgress)
6104 pCbProgress = VDGetInterfaceProgress(pIfProgress);
6105
6106 do {
6107 /* Check arguments. */
6108 AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
6109 rc = VERR_INVALID_PARAMETER);
6110 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE,
6111 ("u32Signature=%08x\n", pDisk->u32Signature));
6112
6113 rc2 = vdThreadStartRead(pDisk);
6114 AssertRC(rc2);
6115 fLockRead = true;
6116
6117 /* Not supported if the disk has child images attached. */
6118 AssertMsgBreakStmt(pDisk->cImages == 1, ("cImages=%u\n", pDisk->cImages),
6119 rc = VERR_NOT_SUPPORTED);
6120
6121 PVDIMAGE pImage = pDisk->pBase;
6122
6123 /* If there is no compact callback for not file based backends then
6124 * the backend doesn't need compaction. No need to make much fuss about
6125 * this. For file based ones signal this as not yet supported. */
6126 if (!pImage->Backend->pfnResize)
6127 {
6128 if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
6129 rc = VERR_NOT_SUPPORTED;
6130 else
6131 rc = VINF_SUCCESS;
6132 break;
6133 }
6134
6135 rc2 = vdThreadFinishRead(pDisk);
6136 AssertRC(rc2);
6137 fLockRead = false;
6138
6139 rc2 = vdThreadStartWrite(pDisk);
6140 AssertRC(rc2);
6141 fLockWrite = true;
6142
6143 VDGEOMETRY PCHSGeometryOld;
6144 VDGEOMETRY LCHSGeometryOld;
6145 PCVDGEOMETRY pPCHSGeometryNew;
6146 PCVDGEOMETRY pLCHSGeometryNew;
6147
6148 if (pPCHSGeometry->cCylinders == 0)
6149 {
6150 /* Auto-detect marker, calculate new value ourself. */
6151 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData, &PCHSGeometryOld);
6152 if (RT_SUCCESS(rc) && (PCHSGeometryOld.cCylinders != 0))
6153 PCHSGeometryOld.cCylinders = RT_MIN(cbSize / 512 / PCHSGeometryOld.cHeads / PCHSGeometryOld.cSectors, 16383);
6154 else if (rc == VERR_VD_GEOMETRY_NOT_SET)
6155 rc = VINF_SUCCESS;
6156
6157 pPCHSGeometryNew = &PCHSGeometryOld;
6158 }
6159 else
6160 pPCHSGeometryNew = pPCHSGeometry;
6161
6162 if (pLCHSGeometry->cCylinders == 0)
6163 {
6164 /* Auto-detect marker, calculate new value ourself. */
6165 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData, &LCHSGeometryOld);
6166 if (RT_SUCCESS(rc) && (LCHSGeometryOld.cCylinders != 0))
6167 LCHSGeometryOld.cCylinders = cbSize / 512 / LCHSGeometryOld.cHeads / LCHSGeometryOld.cSectors;
6168 else if (rc == VERR_VD_GEOMETRY_NOT_SET)
6169 rc = VINF_SUCCESS;
6170
6171 pLCHSGeometryNew = &LCHSGeometryOld;
6172 }
6173 else
6174 pLCHSGeometryNew = pLCHSGeometry;
6175
6176 if (RT_SUCCESS(rc))
6177 rc = pImage->Backend->pfnResize(pImage->pBackendData,
6178 cbSize,
6179 pPCHSGeometryNew,
6180 pLCHSGeometryNew,
6181 0, 99,
6182 pDisk->pVDIfsDisk,
6183 pImage->pVDIfsImage,
6184 pVDIfsOperation);
6185 } while (0);
6186
6187 if (RT_UNLIKELY(fLockWrite))
6188 {
6189 rc2 = vdThreadFinishWrite(pDisk);
6190 AssertRC(rc2);
6191 }
6192 else if (RT_UNLIKELY(fLockRead))
6193 {
6194 rc2 = vdThreadFinishRead(pDisk);
6195 AssertRC(rc2);
6196 }
6197
6198 if (RT_SUCCESS(rc))
6199 {
6200 if (pCbProgress && pCbProgress->pfnProgress)
6201 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
6202 }
6203
6204 LogFlowFunc(("returns %Rrc\n", rc));
6205 return rc;
6206}
6207
6208/**
6209 * Closes the last opened image file in HDD container.
6210 * If previous image file was opened in read-only mode (the normal case) and
6211 * the last opened image is in read-write mode then the previous image will be
6212 * reopened in read/write mode.
6213 *
6214 * @returns VBox status code.
6215 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
6216 * @param pDisk Pointer to HDD container.
6217 * @param fDelete If true, delete the image from the host disk.
6218 */
6219VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete)
6220{
6221 int rc = VINF_SUCCESS;
6222 int rc2;
6223 bool fLockWrite = false;
6224
6225 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
6226 do
6227 {
6228 /* sanity check */
6229 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6230 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6231
6232 /* Not worth splitting this up into a read lock phase and write
6233 * lock phase, as closing an image is a relatively fast operation
6234 * dominated by the part which needs the write lock. */
6235 rc2 = vdThreadStartWrite(pDisk);
6236 AssertRC(rc2);
6237 fLockWrite = true;
6238
6239 PVDIMAGE pImage = pDisk->pLast;
6240 if (!pImage)
6241 {
6242 rc = VERR_VD_NOT_OPENED;
6243 break;
6244 }
6245 unsigned uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
6246 /* Remove image from list of opened images. */
6247 vdRemoveImageFromList(pDisk, pImage);
6248 /* Close (and optionally delete) image. */
6249 rc = pImage->Backend->pfnClose(pImage->pBackendData, fDelete);
6250 /* Free remaining resources related to the image. */
6251 RTStrFree(pImage->pszFilename);
6252 RTMemFree(pImage);
6253
6254 pImage = pDisk->pLast;
6255 if (!pImage)
6256 break;
6257
6258 /* If disk was previously in read/write mode, make sure it will stay
6259 * like this (if possible) after closing this image. Set the open flags
6260 * accordingly. */
6261 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
6262 {
6263 uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
6264 uOpenFlags &= ~ VD_OPEN_FLAGS_READONLY;
6265 rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData, uOpenFlags);
6266 }
6267
6268 /* Cache disk information. */
6269 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
6270
6271 /* Cache PCHS geometry. */
6272 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
6273 &pDisk->PCHSGeometry);
6274 if (RT_FAILURE(rc2))
6275 {
6276 pDisk->PCHSGeometry.cCylinders = 0;
6277 pDisk->PCHSGeometry.cHeads = 0;
6278 pDisk->PCHSGeometry.cSectors = 0;
6279 }
6280 else
6281 {
6282 /* Make sure the PCHS geometry is properly clipped. */
6283 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
6284 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
6285 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
6286 }
6287
6288 /* Cache LCHS geometry. */
6289 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
6290 &pDisk->LCHSGeometry);
6291 if (RT_FAILURE(rc2))
6292 {
6293 pDisk->LCHSGeometry.cCylinders = 0;
6294 pDisk->LCHSGeometry.cHeads = 0;
6295 pDisk->LCHSGeometry.cSectors = 0;
6296 }
6297 else
6298 {
6299 /* Make sure the LCHS geometry is properly clipped. */
6300 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
6301 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
6302 }
6303 } while (0);
6304
6305 if (RT_UNLIKELY(fLockWrite))
6306 {
6307 rc2 = vdThreadFinishWrite(pDisk);
6308 AssertRC(rc2);
6309 }
6310
6311 LogFlowFunc(("returns %Rrc\n", rc));
6312 return rc;
6313}
6314
6315/**
6316 * Closes the currently opened cache image file in HDD container.
6317 *
6318 * @return VBox status code.
6319 * @return VERR_VD_NOT_OPENED if no cache is opened in HDD container.
6320 * @param pDisk Pointer to HDD container.
6321 * @param fDelete If true, delete the image from the host disk.
6322 */
6323VBOXDDU_DECL(int) VDCacheClose(PVBOXHDD pDisk, bool fDelete)
6324{
6325 int rc = VINF_SUCCESS;
6326 int rc2;
6327 bool fLockWrite = false;
6328 PVDCACHE pCache = NULL;
6329
6330 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
6331
6332 do
6333 {
6334 /* sanity check */
6335 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6336 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6337
6338 rc2 = vdThreadStartWrite(pDisk);
6339 AssertRC(rc2);
6340 fLockWrite = true;
6341
6342 AssertPtrBreakStmt(pDisk->pCache, rc = VERR_VD_CACHE_NOT_FOUND);
6343
6344 pCache = pDisk->pCache;
6345 pDisk->pCache = NULL;
6346
6347 pCache->Backend->pfnClose(pCache->pBackendData, fDelete);
6348 if (pCache->pszFilename)
6349 RTStrFree(pCache->pszFilename);
6350 RTMemFree(pCache);
6351 } while (0);
6352
6353 if (RT_LIKELY(fLockWrite))
6354 {
6355 rc2 = vdThreadFinishWrite(pDisk);
6356 AssertRC(rc2);
6357 }
6358
6359 LogFlowFunc(("returns %Rrc\n", rc));
6360 return rc;
6361}
6362
6363/**
6364 * Closes all opened image files in HDD container.
6365 *
6366 * @returns VBox status code.
6367 * @param pDisk Pointer to HDD container.
6368 */
6369VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk)
6370{
6371 int rc = VINF_SUCCESS;
6372 int rc2;
6373 bool fLockWrite = false;
6374
6375 LogFlowFunc(("pDisk=%#p\n", pDisk));
6376 do
6377 {
6378 /* sanity check */
6379 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6380 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6381
6382 /* Lock the entire operation. */
6383 rc2 = vdThreadStartWrite(pDisk);
6384 AssertRC(rc2);
6385 fLockWrite = true;
6386
6387 PVDCACHE pCache = pDisk->pCache;
6388 if (pCache)
6389 {
6390 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, false);
6391 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
6392 rc = rc2;
6393
6394 if (pCache->pszFilename)
6395 RTStrFree(pCache->pszFilename);
6396 RTMemFree(pCache);
6397 }
6398
6399 PVDIMAGE pImage = pDisk->pLast;
6400 while (VALID_PTR(pImage))
6401 {
6402 PVDIMAGE pPrev = pImage->pPrev;
6403 /* Remove image from list of opened images. */
6404 vdRemoveImageFromList(pDisk, pImage);
6405 /* Close image. */
6406 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, false);
6407 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
6408 rc = rc2;
6409 /* Free remaining resources related to the image. */
6410 RTStrFree(pImage->pszFilename);
6411 RTMemFree(pImage);
6412 pImage = pPrev;
6413 }
6414 Assert(!VALID_PTR(pDisk->pLast));
6415 } while (0);
6416
6417 if (RT_UNLIKELY(fLockWrite))
6418 {
6419 rc2 = vdThreadFinishWrite(pDisk);
6420 AssertRC(rc2);
6421 }
6422
6423 LogFlowFunc(("returns %Rrc\n", rc));
6424 return rc;
6425}
6426
6427/**
6428 * Read data from virtual HDD.
6429 *
6430 * @returns VBox status code.
6431 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
6432 * @param pDisk Pointer to HDD container.
6433 * @param uOffset Offset of first reading byte from start of disk.
6434 * @param pvBuf Pointer to buffer for reading data.
6435 * @param cbRead Number of bytes to read.
6436 */
6437VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuf,
6438 size_t cbRead)
6439{
6440 int rc = VINF_SUCCESS;
6441 int rc2;
6442 bool fLockRead = false;
6443
6444 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbRead=%zu\n",
6445 pDisk, uOffset, pvBuf, cbRead));
6446 do
6447 {
6448 /* sanity check */
6449 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6450 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6451
6452 /* Check arguments. */
6453 AssertMsgBreakStmt(VALID_PTR(pvBuf),
6454 ("pvBuf=%#p\n", pvBuf),
6455 rc = VERR_INVALID_PARAMETER);
6456 AssertMsgBreakStmt(cbRead,
6457 ("cbRead=%zu\n", cbRead),
6458 rc = VERR_INVALID_PARAMETER);
6459
6460 rc2 = vdThreadStartRead(pDisk);
6461 AssertRC(rc2);
6462 fLockRead = true;
6463
6464 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
6465 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
6466 uOffset, cbRead, pDisk->cbSize),
6467 rc = VERR_INVALID_PARAMETER);
6468
6469 PVDIMAGE pImage = pDisk->pLast;
6470 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
6471
6472 rc = vdReadHelper(pDisk, pImage, NULL, uOffset, pvBuf, cbRead,
6473 true /* fZeroFreeBlocks */,
6474 true /* fUpdateCache */);
6475 } while (0);
6476
6477 if (RT_UNLIKELY(fLockRead))
6478 {
6479 rc2 = vdThreadFinishRead(pDisk);
6480 AssertRC(rc2);
6481 }
6482
6483 LogFlowFunc(("returns %Rrc\n", rc));
6484 return rc;
6485}
6486
6487/**
6488 * Write data to virtual HDD.
6489 *
6490 * @returns VBox status code.
6491 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
6492 * @param pDisk Pointer to HDD container.
6493 * @param uOffset Offset of the first byte being
6494 * written from start of disk.
6495 * @param pvBuf Pointer to buffer for writing data.
6496 * @param cbWrite Number of bytes to write.
6497 */
6498VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf,
6499 size_t cbWrite)
6500{
6501 int rc = VINF_SUCCESS;
6502 int rc2;
6503 bool fLockWrite = false;
6504
6505 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbWrite=%zu\n",
6506 pDisk, uOffset, pvBuf, cbWrite));
6507 do
6508 {
6509 /* sanity check */
6510 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6511 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6512
6513 /* Check arguments. */
6514 AssertMsgBreakStmt(VALID_PTR(pvBuf),
6515 ("pvBuf=%#p\n", pvBuf),
6516 rc = VERR_INVALID_PARAMETER);
6517 AssertMsgBreakStmt(cbWrite,
6518 ("cbWrite=%zu\n", cbWrite),
6519 rc = VERR_INVALID_PARAMETER);
6520
6521 rc2 = vdThreadStartWrite(pDisk);
6522 AssertRC(rc2);
6523 fLockWrite = true;
6524
6525 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
6526 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
6527 uOffset, cbWrite, pDisk->cbSize),
6528 rc = VERR_INVALID_PARAMETER);
6529
6530 PVDIMAGE pImage = pDisk->pLast;
6531 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
6532
6533 vdSetModifiedFlag(pDisk);
6534 rc = vdWriteHelper(pDisk, pImage, NULL, uOffset, pvBuf, cbWrite,
6535 true /* fUpdateCache */);
6536 if (RT_FAILURE(rc))
6537 break;
6538
6539 /* If there is a merge (in the direction towards a parent) running
6540 * concurrently then we have to also "relay" the write to this parent,
6541 * as the merge position might be already past the position where
6542 * this write is going. The "context" of the write can come from the
6543 * natural chain, since merging either already did or will take care
6544 * of the "other" content which is might be needed to fill the block
6545 * to a full allocation size. The cache doesn't need to be touched
6546 * as this write is covered by the previous one. */
6547 if (RT_UNLIKELY(pDisk->pImageRelay))
6548 rc = vdWriteHelper(pDisk, pDisk->pImageRelay, NULL, uOffset,
6549 pvBuf, cbWrite, false /* fUpdateCache */);
6550 } while (0);
6551
6552 if (RT_UNLIKELY(fLockWrite))
6553 {
6554 rc2 = vdThreadFinishWrite(pDisk);
6555 AssertRC(rc2);
6556 }
6557
6558 LogFlowFunc(("returns %Rrc\n", rc));
6559 return rc;
6560}
6561
6562/**
6563 * Make sure the on disk representation of a virtual HDD is up to date.
6564 *
6565 * @returns VBox status code.
6566 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
6567 * @param pDisk Pointer to HDD container.
6568 */
6569VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
6570{
6571 int rc = VINF_SUCCESS;
6572 int rc2;
6573 bool fLockWrite = false;
6574
6575 LogFlowFunc(("pDisk=%#p\n", pDisk));
6576 do
6577 {
6578 /* sanity check */
6579 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6580 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6581
6582 rc2 = vdThreadStartWrite(pDisk);
6583 AssertRC(rc2);
6584 fLockWrite = true;
6585
6586 PVDIMAGE pImage = pDisk->pLast;
6587 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
6588
6589 vdResetModifiedFlag(pDisk);
6590 rc = pImage->Backend->pfnFlush(pImage->pBackendData);
6591
6592 if ( RT_SUCCESS(rc)
6593 && pDisk->pCache)
6594 rc = pDisk->pCache->Backend->pfnFlush(pDisk->pCache->pBackendData);
6595 } while (0);
6596
6597 if (RT_UNLIKELY(fLockWrite))
6598 {
6599 rc2 = vdThreadFinishWrite(pDisk);
6600 AssertRC(rc2);
6601 }
6602
6603 LogFlowFunc(("returns %Rrc\n", rc));
6604 return rc;
6605}
6606
6607/**
6608 * Get number of opened images in HDD container.
6609 *
6610 * @returns Number of opened images for HDD container. 0 if no images have been opened.
6611 * @param pDisk Pointer to HDD container.
6612 */
6613VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk)
6614{
6615 unsigned cImages;
6616 int rc2;
6617 bool fLockRead = false;
6618
6619 LogFlowFunc(("pDisk=%#p\n", pDisk));
6620 do
6621 {
6622 /* sanity check */
6623 AssertPtrBreakStmt(pDisk, cImages = 0);
6624 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6625
6626 rc2 = vdThreadStartRead(pDisk);
6627 AssertRC(rc2);
6628 fLockRead = true;
6629
6630 cImages = pDisk->cImages;
6631 } while (0);
6632
6633 if (RT_UNLIKELY(fLockRead))
6634 {
6635 rc2 = vdThreadFinishRead(pDisk);
6636 AssertRC(rc2);
6637 }
6638
6639 LogFlowFunc(("returns %u\n", cImages));
6640 return cImages;
6641}
6642
6643/**
6644 * Get read/write mode of HDD container.
6645 *
6646 * @returns Virtual disk ReadOnly status.
6647 * @returns true if no image is opened in HDD container.
6648 * @param pDisk Pointer to HDD container.
6649 */
6650VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
6651{
6652 bool fReadOnly;
6653 int rc2;
6654 bool fLockRead = false;
6655
6656 LogFlowFunc(("pDisk=%#p\n", pDisk));
6657 do
6658 {
6659 /* sanity check */
6660 AssertPtrBreakStmt(pDisk, fReadOnly = false);
6661 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6662
6663 rc2 = vdThreadStartRead(pDisk);
6664 AssertRC(rc2);
6665 fLockRead = true;
6666
6667 PVDIMAGE pImage = pDisk->pLast;
6668 AssertPtrBreakStmt(pImage, fReadOnly = true);
6669
6670 unsigned uOpenFlags;
6671 uOpenFlags = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
6672 fReadOnly = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
6673 } while (0);
6674
6675 if (RT_UNLIKELY(fLockRead))
6676 {
6677 rc2 = vdThreadFinishRead(pDisk);
6678 AssertRC(rc2);
6679 }
6680
6681 LogFlowFunc(("returns %d\n", fReadOnly));
6682 return fReadOnly;
6683}
6684
6685/**
6686 * Get total capacity of an image in HDD container.
6687 *
6688 * @returns Virtual disk size in bytes.
6689 * @returns 0 if no image with specified number was not opened.
6690 * @param pDisk Pointer to HDD container.
6691 * @param nImage Image number, counts from 0. 0 is always base image of container.
6692 */
6693VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk, unsigned nImage)
6694{
6695 uint64_t cbSize;
6696 int rc2;
6697 bool fLockRead = false;
6698
6699 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
6700 do
6701 {
6702 /* sanity check */
6703 AssertPtrBreakStmt(pDisk, cbSize = 0);
6704 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6705
6706 rc2 = vdThreadStartRead(pDisk);
6707 AssertRC(rc2);
6708 fLockRead = true;
6709
6710 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6711 AssertPtrBreakStmt(pImage, cbSize = 0);
6712 cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
6713 } while (0);
6714
6715 if (RT_UNLIKELY(fLockRead))
6716 {
6717 rc2 = vdThreadFinishRead(pDisk);
6718 AssertRC(rc2);
6719 }
6720
6721 LogFlowFunc(("returns %llu\n", cbSize));
6722 return cbSize;
6723}
6724
6725/**
6726 * Get total file size of an image in HDD container.
6727 *
6728 * @returns Virtual disk size in bytes.
6729 * @returns 0 if no image is opened in HDD container.
6730 * @param pDisk Pointer to HDD container.
6731 * @param nImage Image number, counts from 0. 0 is always base image of container.
6732 */
6733VBOXDDU_DECL(uint64_t) VDGetFileSize(PVBOXHDD pDisk, unsigned nImage)
6734{
6735 uint64_t cbSize;
6736 int rc2;
6737 bool fLockRead = false;
6738
6739 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
6740 do
6741 {
6742 /* sanity check */
6743 AssertPtrBreakStmt(pDisk, cbSize = 0);
6744 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6745
6746 rc2 = vdThreadStartRead(pDisk);
6747 AssertRC(rc2);
6748 fLockRead = true;
6749
6750 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6751 AssertPtrBreakStmt(pImage, cbSize = 0);
6752 cbSize = pImage->Backend->pfnGetFileSize(pImage->pBackendData);
6753 } while (0);
6754
6755 if (RT_UNLIKELY(fLockRead))
6756 {
6757 rc2 = vdThreadFinishRead(pDisk);
6758 AssertRC(rc2);
6759 }
6760
6761 LogFlowFunc(("returns %llu\n", cbSize));
6762 return cbSize;
6763}
6764
6765/**
6766 * Get virtual disk PCHS geometry stored in HDD container.
6767 *
6768 * @returns VBox status code.
6769 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
6770 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
6771 * @param pDisk Pointer to HDD container.
6772 * @param nImage Image number, counts from 0. 0 is always base image of container.
6773 * @param pPCHSGeometry Where to store PCHS geometry. Not NULL.
6774 */
6775VBOXDDU_DECL(int) VDGetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
6776 PVDGEOMETRY pPCHSGeometry)
6777{
6778 int rc = VINF_SUCCESS;
6779 int rc2;
6780 bool fLockRead = false;
6781
6782 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p\n",
6783 pDisk, nImage, pPCHSGeometry));
6784 do
6785 {
6786 /* sanity check */
6787 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6788 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6789
6790 /* Check arguments. */
6791 AssertMsgBreakStmt(VALID_PTR(pPCHSGeometry),
6792 ("pPCHSGeometry=%#p\n", pPCHSGeometry),
6793 rc = VERR_INVALID_PARAMETER);
6794
6795 rc2 = vdThreadStartRead(pDisk);
6796 AssertRC(rc2);
6797 fLockRead = true;
6798
6799 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6800 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
6801
6802 if (pImage == pDisk->pLast)
6803 {
6804 /* Use cached information if possible. */
6805 if (pDisk->PCHSGeometry.cCylinders != 0)
6806 *pPCHSGeometry = pDisk->PCHSGeometry;
6807 else
6808 rc = VERR_VD_GEOMETRY_NOT_SET;
6809 }
6810 else
6811 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
6812 pPCHSGeometry);
6813 } while (0);
6814
6815 if (RT_UNLIKELY(fLockRead))
6816 {
6817 rc2 = vdThreadFinishRead(pDisk);
6818 AssertRC(rc2);
6819 }
6820
6821 LogFlowFunc(("%Rrc (PCHS=%u/%u/%u)\n", rc,
6822 pDisk->PCHSGeometry.cCylinders, pDisk->PCHSGeometry.cHeads,
6823 pDisk->PCHSGeometry.cSectors));
6824 return rc;
6825}
6826
6827/**
6828 * Store virtual disk PCHS geometry in HDD container.
6829 *
6830 * Note that in case of unrecoverable error all images in HDD container will be closed.
6831 *
6832 * @returns VBox status code.
6833 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
6834 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
6835 * @param pDisk Pointer to HDD container.
6836 * @param nImage Image number, counts from 0. 0 is always base image of container.
6837 * @param pPCHSGeometry Where to load PCHS geometry from. Not NULL.
6838 */
6839VBOXDDU_DECL(int) VDSetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
6840 PCVDGEOMETRY pPCHSGeometry)
6841{
6842 int rc = VINF_SUCCESS;
6843 int rc2;
6844 bool fLockWrite = false;
6845
6846 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
6847 pDisk, nImage, pPCHSGeometry, pPCHSGeometry->cCylinders,
6848 pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
6849 do
6850 {
6851 /* sanity check */
6852 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6853 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6854
6855 /* Check arguments. */
6856 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
6857 && pPCHSGeometry->cHeads <= 16
6858 && pPCHSGeometry->cSectors <= 63,
6859 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
6860 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
6861 pPCHSGeometry->cSectors),
6862 rc = VERR_INVALID_PARAMETER);
6863
6864 rc2 = vdThreadStartWrite(pDisk);
6865 AssertRC(rc2);
6866 fLockWrite = true;
6867
6868 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6869 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
6870
6871 if (pImage == pDisk->pLast)
6872 {
6873 if ( pPCHSGeometry->cCylinders != pDisk->PCHSGeometry.cCylinders
6874 || pPCHSGeometry->cHeads != pDisk->PCHSGeometry.cHeads
6875 || pPCHSGeometry->cSectors != pDisk->PCHSGeometry.cSectors)
6876 {
6877 /* Only update geometry if it is changed. Avoids similar checks
6878 * in every backend. Most of the time the new geometry is set
6879 * to the previous values, so no need to go through the hassle
6880 * of updating an image which could be opened in read-only mode
6881 * right now. */
6882 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pBackendData,
6883 pPCHSGeometry);
6884
6885 /* Cache new geometry values in any case. */
6886 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
6887 &pDisk->PCHSGeometry);
6888 if (RT_FAILURE(rc2))
6889 {
6890 pDisk->PCHSGeometry.cCylinders = 0;
6891 pDisk->PCHSGeometry.cHeads = 0;
6892 pDisk->PCHSGeometry.cSectors = 0;
6893 }
6894 else
6895 {
6896 /* Make sure the CHS geometry is properly clipped. */
6897 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 255);
6898 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
6899 }
6900 }
6901 }
6902 else
6903 {
6904 VDGEOMETRY PCHS;
6905 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
6906 &PCHS);
6907 if ( RT_FAILURE(rc)
6908 || pPCHSGeometry->cCylinders != PCHS.cCylinders
6909 || pPCHSGeometry->cHeads != PCHS.cHeads
6910 || pPCHSGeometry->cSectors != PCHS.cSectors)
6911 {
6912 /* Only update geometry if it is changed. Avoids similar checks
6913 * in every backend. Most of the time the new geometry is set
6914 * to the previous values, so no need to go through the hassle
6915 * of updating an image which could be opened in read-only mode
6916 * right now. */
6917 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pBackendData,
6918 pPCHSGeometry);
6919 }
6920 }
6921 } while (0);
6922
6923 if (RT_UNLIKELY(fLockWrite))
6924 {
6925 rc2 = vdThreadFinishWrite(pDisk);
6926 AssertRC(rc2);
6927 }
6928
6929 LogFlowFunc(("returns %Rrc\n", rc));
6930 return rc;
6931}
6932
6933/**
6934 * Get virtual disk LCHS geometry stored in HDD container.
6935 *
6936 * @returns VBox status code.
6937 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
6938 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
6939 * @param pDisk Pointer to HDD container.
6940 * @param nImage Image number, counts from 0. 0 is always base image of container.
6941 * @param pLCHSGeometry Where to store LCHS geometry. Not NULL.
6942 */
6943VBOXDDU_DECL(int) VDGetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
6944 PVDGEOMETRY pLCHSGeometry)
6945{
6946 int rc = VINF_SUCCESS;
6947 int rc2;
6948 bool fLockRead = false;
6949
6950 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p\n",
6951 pDisk, nImage, pLCHSGeometry));
6952 do
6953 {
6954 /* sanity check */
6955 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6956 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6957
6958 /* Check arguments. */
6959 AssertMsgBreakStmt(VALID_PTR(pLCHSGeometry),
6960 ("pLCHSGeometry=%#p\n", pLCHSGeometry),
6961 rc = VERR_INVALID_PARAMETER);
6962
6963 rc2 = vdThreadStartRead(pDisk);
6964 AssertRC(rc2);
6965 fLockRead = true;
6966
6967 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6968 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
6969
6970 if (pImage == pDisk->pLast)
6971 {
6972 /* Use cached information if possible. */
6973 if (pDisk->LCHSGeometry.cCylinders != 0)
6974 *pLCHSGeometry = pDisk->LCHSGeometry;
6975 else
6976 rc = VERR_VD_GEOMETRY_NOT_SET;
6977 }
6978 else
6979 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
6980 pLCHSGeometry);
6981 } while (0);
6982
6983 if (RT_UNLIKELY(fLockRead))
6984 {
6985 rc2 = vdThreadFinishRead(pDisk);
6986 AssertRC(rc2);
6987 }
6988
6989 LogFlowFunc((": %Rrc (LCHS=%u/%u/%u)\n", rc,
6990 pDisk->LCHSGeometry.cCylinders, pDisk->LCHSGeometry.cHeads,
6991 pDisk->LCHSGeometry.cSectors));
6992 return rc;
6993}
6994
6995/**
6996 * Store virtual disk LCHS geometry in HDD container.
6997 *
6998 * Note that in case of unrecoverable error all images in HDD container will be closed.
6999 *
7000 * @returns VBox status code.
7001 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7002 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
7003 * @param pDisk Pointer to HDD container.
7004 * @param nImage Image number, counts from 0. 0 is always base image of container.
7005 * @param pLCHSGeometry Where to load LCHS geometry from. Not NULL.
7006 */
7007VBOXDDU_DECL(int) VDSetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
7008 PCVDGEOMETRY pLCHSGeometry)
7009{
7010 int rc = VINF_SUCCESS;
7011 int rc2;
7012 bool fLockWrite = false;
7013
7014 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
7015 pDisk, nImage, pLCHSGeometry, pLCHSGeometry->cCylinders,
7016 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
7017 do
7018 {
7019 /* sanity check */
7020 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7021 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7022
7023 /* Check arguments. */
7024 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
7025 && pLCHSGeometry->cHeads <= 255
7026 && pLCHSGeometry->cSectors <= 63,
7027 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
7028 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
7029 pLCHSGeometry->cSectors),
7030 rc = VERR_INVALID_PARAMETER);
7031
7032 rc2 = vdThreadStartWrite(pDisk);
7033 AssertRC(rc2);
7034 fLockWrite = true;
7035
7036 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7037 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7038
7039 if (pImage == pDisk->pLast)
7040 {
7041 if ( pLCHSGeometry->cCylinders != pDisk->LCHSGeometry.cCylinders
7042 || pLCHSGeometry->cHeads != pDisk->LCHSGeometry.cHeads
7043 || pLCHSGeometry->cSectors != pDisk->LCHSGeometry.cSectors)
7044 {
7045 /* Only update geometry if it is changed. Avoids similar checks
7046 * in every backend. Most of the time the new geometry is set
7047 * to the previous values, so no need to go through the hassle
7048 * of updating an image which could be opened in read-only mode
7049 * right now. */
7050 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pBackendData,
7051 pLCHSGeometry);
7052
7053 /* Cache new geometry values in any case. */
7054 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
7055 &pDisk->LCHSGeometry);
7056 if (RT_FAILURE(rc2))
7057 {
7058 pDisk->LCHSGeometry.cCylinders = 0;
7059 pDisk->LCHSGeometry.cHeads = 0;
7060 pDisk->LCHSGeometry.cSectors = 0;
7061 }
7062 else
7063 {
7064 /* Make sure the CHS geometry is properly clipped. */
7065 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
7066 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
7067 }
7068 }
7069 }
7070 else
7071 {
7072 VDGEOMETRY LCHS;
7073 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
7074 &LCHS);
7075 if ( RT_FAILURE(rc)
7076 || pLCHSGeometry->cCylinders != LCHS.cCylinders
7077 || pLCHSGeometry->cHeads != LCHS.cHeads
7078 || pLCHSGeometry->cSectors != LCHS.cSectors)
7079 {
7080 /* Only update geometry if it is changed. Avoids similar checks
7081 * in every backend. Most of the time the new geometry is set
7082 * to the previous values, so no need to go through the hassle
7083 * of updating an image which could be opened in read-only mode
7084 * right now. */
7085 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pBackendData,
7086 pLCHSGeometry);
7087 }
7088 }
7089 } while (0);
7090
7091 if (RT_UNLIKELY(fLockWrite))
7092 {
7093 rc2 = vdThreadFinishWrite(pDisk);
7094 AssertRC(rc2);
7095 }
7096
7097 LogFlowFunc(("returns %Rrc\n", rc));
7098 return rc;
7099}
7100
7101/**
7102 * Get version of image in HDD container.
7103 *
7104 * @returns VBox status code.
7105 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7106 * @param pDisk Pointer to HDD container.
7107 * @param nImage Image number, counts from 0. 0 is always base image of container.
7108 * @param puVersion Where to store the image version.
7109 */
7110VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
7111 unsigned *puVersion)
7112{
7113 int rc = VINF_SUCCESS;
7114 int rc2;
7115 bool fLockRead = false;
7116
7117 LogFlowFunc(("pDisk=%#p nImage=%u puVersion=%#p\n",
7118 pDisk, nImage, puVersion));
7119 do
7120 {
7121 /* sanity check */
7122 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7123 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7124
7125 /* Check arguments. */
7126 AssertMsgBreakStmt(VALID_PTR(puVersion),
7127 ("puVersion=%#p\n", puVersion),
7128 rc = VERR_INVALID_PARAMETER);
7129
7130 rc2 = vdThreadStartRead(pDisk);
7131 AssertRC(rc2);
7132 fLockRead = true;
7133
7134 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7135 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7136
7137 *puVersion = pImage->Backend->pfnGetVersion(pImage->pBackendData);
7138 } while (0);
7139
7140 if (RT_UNLIKELY(fLockRead))
7141 {
7142 rc2 = vdThreadFinishRead(pDisk);
7143 AssertRC(rc2);
7144 }
7145
7146 LogFlowFunc(("returns %Rrc uVersion=%#x\n", rc, *puVersion));
7147 return rc;
7148}
7149
7150/**
7151 * List the capabilities of image backend in HDD container.
7152 *
7153 * @returns VBox status code.
7154 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7155 * @param pDisk Pointer to the HDD container.
7156 * @param nImage Image number, counts from 0. 0 is always base image of container.
7157 * @param pbackendInfo Where to store the backend information.
7158 */
7159VBOXDDU_DECL(int) VDBackendInfoSingle(PVBOXHDD pDisk, unsigned nImage,
7160 PVDBACKENDINFO pBackendInfo)
7161{
7162 int rc = VINF_SUCCESS;
7163 int rc2;
7164 bool fLockRead = false;
7165
7166 LogFlowFunc(("pDisk=%#p nImage=%u pBackendInfo=%#p\n",
7167 pDisk, nImage, pBackendInfo));
7168 do
7169 {
7170 /* sanity check */
7171 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7172 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7173
7174 /* Check arguments. */
7175 AssertMsgBreakStmt(VALID_PTR(pBackendInfo),
7176 ("pBackendInfo=%#p\n", pBackendInfo),
7177 rc = VERR_INVALID_PARAMETER);
7178
7179 rc2 = vdThreadStartRead(pDisk);
7180 AssertRC(rc2);
7181 fLockRead = true;
7182
7183 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7184 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7185
7186 pBackendInfo->pszBackend = pImage->Backend->pszBackendName;
7187 pBackendInfo->uBackendCaps = pImage->Backend->uBackendCaps;
7188 pBackendInfo->paFileExtensions = pImage->Backend->paFileExtensions;
7189 pBackendInfo->paConfigInfo = pImage->Backend->paConfigInfo;
7190 } while (0);
7191
7192 if (RT_UNLIKELY(fLockRead))
7193 {
7194 rc2 = vdThreadFinishRead(pDisk);
7195 AssertRC(rc2);
7196 }
7197
7198 LogFlowFunc(("returns %Rrc\n", rc));
7199 return rc;
7200}
7201
7202/**
7203 * Get flags of image in HDD container.
7204 *
7205 * @returns VBox status code.
7206 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7207 * @param pDisk Pointer to HDD container.
7208 * @param nImage Image number, counts from 0. 0 is always base image of container.
7209 * @param puImageFlags Where to store the image flags.
7210 */
7211VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage,
7212 unsigned *puImageFlags)
7213{
7214 int rc = VINF_SUCCESS;
7215 int rc2;
7216 bool fLockRead = false;
7217
7218 LogFlowFunc(("pDisk=%#p nImage=%u puImageFlags=%#p\n",
7219 pDisk, nImage, puImageFlags));
7220 do
7221 {
7222 /* sanity check */
7223 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7224 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7225
7226 /* Check arguments. */
7227 AssertMsgBreakStmt(VALID_PTR(puImageFlags),
7228 ("puImageFlags=%#p\n", puImageFlags),
7229 rc = VERR_INVALID_PARAMETER);
7230
7231 rc2 = vdThreadStartRead(pDisk);
7232 AssertRC(rc2);
7233 fLockRead = true;
7234
7235 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7236 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7237
7238 *puImageFlags = pImage->uImageFlags;
7239 } while (0);
7240
7241 if (RT_UNLIKELY(fLockRead))
7242 {
7243 rc2 = vdThreadFinishRead(pDisk);
7244 AssertRC(rc2);
7245 }
7246
7247 LogFlowFunc(("returns %Rrc uImageFlags=%#x\n", rc, *puImageFlags));
7248 return rc;
7249}
7250
7251/**
7252 * Get open flags of image in HDD container.
7253 *
7254 * @returns VBox status code.
7255 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7256 * @param pDisk Pointer to HDD container.
7257 * @param nImage Image number, counts from 0. 0 is always base image of container.
7258 * @param puOpenFlags Where to store the image open flags.
7259 */
7260VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
7261 unsigned *puOpenFlags)
7262{
7263 int rc = VINF_SUCCESS;
7264 int rc2;
7265 bool fLockRead = false;
7266
7267 LogFlowFunc(("pDisk=%#p nImage=%u puOpenFlags=%#p\n",
7268 pDisk, nImage, puOpenFlags));
7269 do
7270 {
7271 /* sanity check */
7272 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7273 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7274
7275 /* Check arguments. */
7276 AssertMsgBreakStmt(VALID_PTR(puOpenFlags),
7277 ("puOpenFlags=%#p\n", puOpenFlags),
7278 rc = VERR_INVALID_PARAMETER);
7279
7280 rc2 = vdThreadStartRead(pDisk);
7281 AssertRC(rc2);
7282 fLockRead = true;
7283
7284 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7285 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7286
7287 *puOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
7288 } while (0);
7289
7290 if (RT_UNLIKELY(fLockRead))
7291 {
7292 rc2 = vdThreadFinishRead(pDisk);
7293 AssertRC(rc2);
7294 }
7295
7296 LogFlowFunc(("returns %Rrc uOpenFlags=%#x\n", rc, *puOpenFlags));
7297 return rc;
7298}
7299
7300/**
7301 * Set open flags of image in HDD container.
7302 * This operation may cause file locking changes and/or files being reopened.
7303 * Note that in case of unrecoverable error all images in HDD container will be closed.
7304 *
7305 * @returns VBox status code.
7306 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7307 * @param pDisk Pointer to HDD container.
7308 * @param nImage Image number, counts from 0. 0 is always base image of container.
7309 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
7310 */
7311VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
7312 unsigned uOpenFlags)
7313{
7314 int rc;
7315 int rc2;
7316 bool fLockWrite = false;
7317
7318 LogFlowFunc(("pDisk=%#p uOpenFlags=%#u\n", pDisk, uOpenFlags));
7319 do
7320 {
7321 /* sanity check */
7322 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7323 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7324
7325 /* Check arguments. */
7326 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
7327 ("uOpenFlags=%#x\n", uOpenFlags),
7328 rc = VERR_INVALID_PARAMETER);
7329
7330 rc2 = vdThreadStartWrite(pDisk);
7331 AssertRC(rc2);
7332 fLockWrite = true;
7333
7334 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7335 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7336
7337 rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData,
7338 uOpenFlags);
7339 } while (0);
7340
7341 if (RT_UNLIKELY(fLockWrite))
7342 {
7343 rc2 = vdThreadFinishWrite(pDisk);
7344 AssertRC(rc2);
7345 }
7346
7347 LogFlowFunc(("returns %Rrc\n", rc));
7348 return rc;
7349}
7350
7351/**
7352 * Get base filename of image in HDD container. Some image formats use
7353 * other filenames as well, so don't use this for anything but informational
7354 * purposes.
7355 *
7356 * @returns VBox status code.
7357 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7358 * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
7359 * @param pDisk Pointer to HDD container.
7360 * @param nImage Image number, counts from 0. 0 is always base image of container.
7361 * @param pszFilename Where to store the image file name.
7362 * @param cbFilename Size of buffer pszFilename points to.
7363 */
7364VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
7365 char *pszFilename, unsigned cbFilename)
7366{
7367 int rc;
7368 int rc2;
7369 bool fLockRead = false;
7370
7371 LogFlowFunc(("pDisk=%#p nImage=%u pszFilename=%#p cbFilename=%u\n",
7372 pDisk, nImage, pszFilename, cbFilename));
7373 do
7374 {
7375 /* sanity check */
7376 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7377 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7378
7379 /* Check arguments. */
7380 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
7381 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
7382 rc = VERR_INVALID_PARAMETER);
7383 AssertMsgBreakStmt(cbFilename,
7384 ("cbFilename=%u\n", cbFilename),
7385 rc = VERR_INVALID_PARAMETER);
7386
7387 rc2 = vdThreadStartRead(pDisk);
7388 AssertRC(rc2);
7389 fLockRead = true;
7390
7391 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7392 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7393
7394 size_t cb = strlen(pImage->pszFilename);
7395 if (cb <= cbFilename)
7396 {
7397 strcpy(pszFilename, pImage->pszFilename);
7398 rc = VINF_SUCCESS;
7399 }
7400 else
7401 {
7402 strncpy(pszFilename, pImage->pszFilename, cbFilename - 1);
7403 pszFilename[cbFilename - 1] = '\0';
7404 rc = VERR_BUFFER_OVERFLOW;
7405 }
7406 } while (0);
7407
7408 if (RT_UNLIKELY(fLockRead))
7409 {
7410 rc2 = vdThreadFinishRead(pDisk);
7411 AssertRC(rc2);
7412 }
7413
7414 LogFlowFunc(("returns %Rrc, pszFilename=\"%s\"\n", rc, pszFilename));
7415 return rc;
7416}
7417
7418/**
7419 * Get the comment line of image in HDD container.
7420 *
7421 * @returns VBox status code.
7422 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7423 * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
7424 * @param pDisk Pointer to HDD container.
7425 * @param nImage Image number, counts from 0. 0 is always base image of container.
7426 * @param pszComment Where to store the comment string of image. NULL is ok.
7427 * @param cbComment The size of pszComment buffer. 0 is ok.
7428 */
7429VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
7430 char *pszComment, unsigned cbComment)
7431{
7432 int rc;
7433 int rc2;
7434 bool fLockRead = false;
7435
7436 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p cbComment=%u\n",
7437 pDisk, nImage, pszComment, cbComment));
7438 do
7439 {
7440 /* sanity check */
7441 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7442 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7443
7444 /* Check arguments. */
7445 AssertMsgBreakStmt(VALID_PTR(pszComment),
7446 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
7447 rc = VERR_INVALID_PARAMETER);
7448 AssertMsgBreakStmt(cbComment,
7449 ("cbComment=%u\n", cbComment),
7450 rc = VERR_INVALID_PARAMETER);
7451
7452 rc2 = vdThreadStartRead(pDisk);
7453 AssertRC(rc2);
7454 fLockRead = true;
7455
7456 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7457 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7458
7459 rc = pImage->Backend->pfnGetComment(pImage->pBackendData, pszComment,
7460 cbComment);
7461 } while (0);
7462
7463 if (RT_UNLIKELY(fLockRead))
7464 {
7465 rc2 = vdThreadFinishRead(pDisk);
7466 AssertRC(rc2);
7467 }
7468
7469 LogFlowFunc(("returns %Rrc, pszComment=\"%s\"\n", rc, pszComment));
7470 return rc;
7471}
7472
7473/**
7474 * Changes the comment line of image in HDD container.
7475 *
7476 * @returns VBox status code.
7477 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7478 * @param pDisk Pointer to HDD container.
7479 * @param nImage Image number, counts from 0. 0 is always base image of container.
7480 * @param pszComment New comment string (UTF-8). NULL is allowed to reset the comment.
7481 */
7482VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
7483 const char *pszComment)
7484{
7485 int rc;
7486 int rc2;
7487 bool fLockWrite = false;
7488
7489 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p \"%s\"\n",
7490 pDisk, nImage, pszComment, pszComment));
7491 do
7492 {
7493 /* sanity check */
7494 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7495 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7496
7497 /* Check arguments. */
7498 AssertMsgBreakStmt(VALID_PTR(pszComment) || pszComment == NULL,
7499 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
7500 rc = VERR_INVALID_PARAMETER);
7501
7502 rc2 = vdThreadStartWrite(pDisk);
7503 AssertRC(rc2);
7504 fLockWrite = true;
7505
7506 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7507 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7508
7509 rc = pImage->Backend->pfnSetComment(pImage->pBackendData, pszComment);
7510 } while (0);
7511
7512 if (RT_UNLIKELY(fLockWrite))
7513 {
7514 rc2 = vdThreadFinishWrite(pDisk);
7515 AssertRC(rc2);
7516 }
7517
7518 LogFlowFunc(("returns %Rrc\n", rc));
7519 return rc;
7520}
7521
7522
7523/**
7524 * Get UUID of image in HDD container.
7525 *
7526 * @returns VBox status code.
7527 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7528 * @param pDisk Pointer to HDD container.
7529 * @param nImage Image number, counts from 0. 0 is always base image of container.
7530 * @param pUuid Where to store the image creation UUID.
7531 */
7532VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
7533{
7534 int rc;
7535 int rc2;
7536 bool fLockRead = false;
7537
7538 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
7539 do
7540 {
7541 /* sanity check */
7542 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7543 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7544
7545 /* Check arguments. */
7546 AssertMsgBreakStmt(VALID_PTR(pUuid),
7547 ("pUuid=%#p\n", pUuid),
7548 rc = VERR_INVALID_PARAMETER);
7549
7550 rc2 = vdThreadStartRead(pDisk);
7551 AssertRC(rc2);
7552 fLockRead = true;
7553
7554 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7555 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7556
7557 rc = pImage->Backend->pfnGetUuid(pImage->pBackendData, pUuid);
7558 } while (0);
7559
7560 if (RT_UNLIKELY(fLockRead))
7561 {
7562 rc2 = vdThreadFinishRead(pDisk);
7563 AssertRC(rc2);
7564 }
7565
7566 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
7567 return rc;
7568}
7569
7570/**
7571 * Set the image's UUID. Should not be used by normal applications.
7572 *
7573 * @returns VBox status code.
7574 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7575 * @param pDisk Pointer to HDD container.
7576 * @param nImage Image number, counts from 0. 0 is always base image of container.
7577 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
7578 */
7579VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
7580{
7581 int rc;
7582 int rc2;
7583 bool fLockWrite = false;
7584
7585 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
7586 pDisk, nImage, pUuid, pUuid));
7587 do
7588 {
7589 /* sanity check */
7590 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7591 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7592
7593 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
7594 ("pUuid=%#p\n", pUuid),
7595 rc = VERR_INVALID_PARAMETER);
7596
7597 rc2 = vdThreadStartWrite(pDisk);
7598 AssertRC(rc2);
7599 fLockWrite = true;
7600
7601 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7602 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7603
7604 RTUUID Uuid;
7605 if (!pUuid)
7606 {
7607 RTUuidCreate(&Uuid);
7608 pUuid = &Uuid;
7609 }
7610 rc = pImage->Backend->pfnSetUuid(pImage->pBackendData, pUuid);
7611 } while (0);
7612
7613 if (RT_UNLIKELY(fLockWrite))
7614 {
7615 rc2 = vdThreadFinishWrite(pDisk);
7616 AssertRC(rc2);
7617 }
7618
7619 LogFlowFunc(("returns %Rrc\n", rc));
7620 return rc;
7621}
7622
7623/**
7624 * Get last modification UUID of image in HDD container.
7625 *
7626 * @returns VBox status code.
7627 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7628 * @param pDisk Pointer to HDD container.
7629 * @param nImage Image number, counts from 0. 0 is always base image of container.
7630 * @param pUuid Where to store the image modification UUID.
7631 */
7632VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
7633{
7634 int rc = VINF_SUCCESS;
7635 int rc2;
7636 bool fLockRead = false;
7637
7638 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
7639 do
7640 {
7641 /* sanity check */
7642 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7643 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7644
7645 /* Check arguments. */
7646 AssertMsgBreakStmt(VALID_PTR(pUuid),
7647 ("pUuid=%#p\n", pUuid),
7648 rc = VERR_INVALID_PARAMETER);
7649
7650 rc2 = vdThreadStartRead(pDisk);
7651 AssertRC(rc2);
7652 fLockRead = true;
7653
7654 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7655 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7656
7657 rc = pImage->Backend->pfnGetModificationUuid(pImage->pBackendData,
7658 pUuid);
7659 } while (0);
7660
7661 if (RT_UNLIKELY(fLockRead))
7662 {
7663 rc2 = vdThreadFinishRead(pDisk);
7664 AssertRC(rc2);
7665 }
7666
7667 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
7668 return rc;
7669}
7670
7671/**
7672 * Set the image's last modification UUID. Should not be used by normal applications.
7673 *
7674 * @returns VBox status code.
7675 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7676 * @param pDisk Pointer to HDD container.
7677 * @param nImage Image number, counts from 0. 0 is always base image of container.
7678 * @param pUuid New modification UUID of the image. If NULL, a new UUID is created.
7679 */
7680VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
7681{
7682 int rc;
7683 int rc2;
7684 bool fLockWrite = false;
7685
7686 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
7687 pDisk, nImage, pUuid, pUuid));
7688 do
7689 {
7690 /* sanity check */
7691 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7692 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7693
7694 /* Check arguments. */
7695 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
7696 ("pUuid=%#p\n", pUuid),
7697 rc = VERR_INVALID_PARAMETER);
7698
7699 rc2 = vdThreadStartWrite(pDisk);
7700 AssertRC(rc2);
7701 fLockWrite = true;
7702
7703 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7704 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7705
7706 RTUUID Uuid;
7707 if (!pUuid)
7708 {
7709 RTUuidCreate(&Uuid);
7710 pUuid = &Uuid;
7711 }
7712 rc = pImage->Backend->pfnSetModificationUuid(pImage->pBackendData,
7713 pUuid);
7714 } while (0);
7715
7716 if (RT_UNLIKELY(fLockWrite))
7717 {
7718 rc2 = vdThreadFinishWrite(pDisk);
7719 AssertRC(rc2);
7720 }
7721
7722 LogFlowFunc(("returns %Rrc\n", rc));
7723 return rc;
7724}
7725
7726/**
7727 * Get parent UUID of image in HDD container.
7728 *
7729 * @returns VBox status code.
7730 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7731 * @param pDisk Pointer to HDD container.
7732 * @param nImage Image number, counts from 0. 0 is always base image of container.
7733 * @param pUuid Where to store the parent image UUID.
7734 */
7735VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
7736 PRTUUID pUuid)
7737{
7738 int rc = VINF_SUCCESS;
7739 int rc2;
7740 bool fLockRead = false;
7741
7742 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
7743 do
7744 {
7745 /* sanity check */
7746 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7747 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7748
7749 /* Check arguments. */
7750 AssertMsgBreakStmt(VALID_PTR(pUuid),
7751 ("pUuid=%#p\n", pUuid),
7752 rc = VERR_INVALID_PARAMETER);
7753
7754 rc2 = vdThreadStartRead(pDisk);
7755 AssertRC(rc2);
7756 fLockRead = true;
7757
7758 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7759 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7760
7761 rc = pImage->Backend->pfnGetParentUuid(pImage->pBackendData, pUuid);
7762 } while (0);
7763
7764 if (RT_UNLIKELY(fLockRead))
7765 {
7766 rc2 = vdThreadFinishRead(pDisk);
7767 AssertRC(rc2);
7768 }
7769
7770 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
7771 return rc;
7772}
7773
7774/**
7775 * Set the image's parent UUID. Should not be used by normal applications.
7776 *
7777 * @returns VBox status code.
7778 * @param pDisk Pointer to HDD container.
7779 * @param nImage Image number, counts from 0. 0 is always base image of container.
7780 * @param pUuid New parent UUID of the image. If NULL, a new UUID is created.
7781 */
7782VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
7783 PCRTUUID pUuid)
7784{
7785 int rc;
7786 int rc2;
7787 bool fLockWrite = false;
7788
7789 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
7790 pDisk, nImage, pUuid, pUuid));
7791 do
7792 {
7793 /* sanity check */
7794 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7795 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7796
7797 /* Check arguments. */
7798 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
7799 ("pUuid=%#p\n", pUuid),
7800 rc = VERR_INVALID_PARAMETER);
7801
7802 rc2 = vdThreadStartWrite(pDisk);
7803 AssertRC(rc2);
7804 fLockWrite = true;
7805
7806 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7807 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7808
7809 RTUUID Uuid;
7810 if (!pUuid)
7811 {
7812 RTUuidCreate(&Uuid);
7813 pUuid = &Uuid;
7814 }
7815 rc = pImage->Backend->pfnSetParentUuid(pImage->pBackendData, pUuid);
7816 } while (0);
7817
7818 if (RT_UNLIKELY(fLockWrite))
7819 {
7820 rc2 = vdThreadFinishWrite(pDisk);
7821 AssertRC(rc2);
7822 }
7823
7824 LogFlowFunc(("returns %Rrc\n", rc));
7825 return rc;
7826}
7827
7828
7829/**
7830 * Debug helper - dumps all opened images in HDD container into the log file.
7831 *
7832 * @param pDisk Pointer to HDD container.
7833 */
7834VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk)
7835{
7836 int rc2;
7837 bool fLockRead = false;
7838
7839 do
7840 {
7841 /* sanity check */
7842 AssertPtrBreak(pDisk);
7843 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7844
7845 if (!pDisk->pInterfaceErrorCallbacks || !VALID_PTR(pDisk->pInterfaceErrorCallbacks->pfnMessage))
7846 pDisk->pInterfaceErrorCallbacks->pfnMessage = vdLogMessage;
7847
7848 rc2 = vdThreadStartRead(pDisk);
7849 AssertRC(rc2);
7850 fLockRead = true;
7851
7852 vdMessageWrapper(pDisk, "--- Dumping VD Disk, Images=%u\n", pDisk->cImages);
7853 for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
7854 {
7855 vdMessageWrapper(pDisk, "Dumping VD image \"%s\" (Backend=%s)\n",
7856 pImage->pszFilename, pImage->Backend->pszBackendName);
7857 pImage->Backend->pfnDump(pImage->pBackendData);
7858 }
7859 } while (0);
7860
7861 if (RT_UNLIKELY(fLockRead))
7862 {
7863 rc2 = vdThreadFinishRead(pDisk);
7864 AssertRC(rc2);
7865 }
7866}
7867
7868/**
7869 * Query if asynchronous operations are supported for this disk.
7870 *
7871 * @returns VBox status code.
7872 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7873 * @param pDisk Pointer to the HDD container.
7874 * @param nImage Image number, counts from 0. 0 is always base image of container.
7875 * @param pfAIOSupported Where to store if async IO is supported.
7876 */
7877VBOXDDU_DECL(int) VDImageIsAsyncIOSupported(PVBOXHDD pDisk, unsigned nImage, bool *pfAIOSupported)
7878{
7879 int rc = VINF_SUCCESS;
7880 int rc2;
7881 bool fLockRead = false;
7882
7883 LogFlowFunc(("pDisk=%#p nImage=%u pfAIOSupported=%#p\n", pDisk, nImage, pfAIOSupported));
7884 do
7885 {
7886 /* sanity check */
7887 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7888 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7889
7890 /* Check arguments. */
7891 AssertMsgBreakStmt(VALID_PTR(pfAIOSupported),
7892 ("pfAIOSupported=%#p\n", pfAIOSupported),
7893 rc = VERR_INVALID_PARAMETER);
7894
7895 rc2 = vdThreadStartRead(pDisk);
7896 AssertRC(rc2);
7897 fLockRead = true;
7898
7899 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7900 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7901
7902 if (pImage->Backend->uBackendCaps & VD_CAP_ASYNC)
7903 *pfAIOSupported = pImage->Backend->pfnIsAsyncIOSupported(pImage->pBackendData);
7904 else
7905 *pfAIOSupported = false;
7906 } while (0);
7907
7908 if (RT_UNLIKELY(fLockRead))
7909 {
7910 rc2 = vdThreadFinishRead(pDisk);
7911 AssertRC(rc2);
7912 }
7913
7914 LogFlowFunc(("returns %Rrc, fAIOSupported=%u\n", rc, *pfAIOSupported));
7915 return rc;
7916}
7917
7918
7919VBOXDDU_DECL(int) VDAsyncRead(PVBOXHDD pDisk, uint64_t uOffset, size_t cbRead,
7920 PCRTSGSEG paSeg, unsigned cSeg,
7921 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
7922 void *pvUser1, void *pvUser2)
7923{
7924 int rc = VERR_VD_BLOCK_FREE;
7925 int rc2;
7926 bool fLockRead = false;
7927 PVDIOCTX pIoCtx = NULL;
7928
7929 LogFlowFunc(("pDisk=%#p uOffset=%llu paSeg=%p cSeg=%u cbRead=%zu pvUser1=%#p pvUser2=%#p\n",
7930 pDisk, uOffset, paSeg, cSeg, cbRead, pvUser1, pvUser2));
7931
7932 do
7933 {
7934 /* sanity check */
7935 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7936 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7937
7938 /* Check arguments. */
7939 AssertMsgBreakStmt(cbRead,
7940 ("cbRead=%zu\n", cbRead),
7941 rc = VERR_INVALID_PARAMETER);
7942 AssertMsgBreakStmt(VALID_PTR(paSeg),
7943 ("paSeg=%#p\n", paSeg),
7944 rc = VERR_INVALID_PARAMETER);
7945 AssertMsgBreakStmt(cSeg,
7946 ("cSeg=%zu\n", cSeg),
7947 rc = VERR_INVALID_PARAMETER);
7948
7949 rc2 = vdThreadStartRead(pDisk);
7950 AssertRC(rc2);
7951 fLockRead = true;
7952
7953 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
7954 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
7955 uOffset, cbRead, pDisk->cbSize),
7956 rc = VERR_INVALID_PARAMETER);
7957
7958 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_READ, uOffset,
7959 cbRead, paSeg, cSeg,
7960 pfnComplete, pvUser1, pvUser2,
7961 NULL, vdReadHelperAsync);
7962 if (!pIoCtx)
7963 {
7964 rc = VERR_NO_MEMORY;
7965 break;
7966 }
7967
7968 pIoCtx->pImage = pDisk->pLast;
7969 AssertPtrBreakStmt(pIoCtx->pImage, rc = VERR_VD_NOT_OPENED);
7970
7971 rc = vdIoCtxProcess(pIoCtx);
7972 if (rc == VINF_VD_ASYNC_IO_FINISHED)
7973 {
7974 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
7975 vdIoCtxFree(pDisk, pIoCtx);
7976 else
7977 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
7978 }
7979 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
7980 vdIoCtxFree(pDisk, pIoCtx);
7981
7982 } while (0);
7983
7984 if (RT_UNLIKELY(fLockRead) && ( rc == VINF_VD_ASYNC_IO_FINISHED
7985 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
7986 {
7987 rc2 = vdThreadFinishRead(pDisk);
7988 AssertRC(rc2);
7989 }
7990
7991 LogFlowFunc(("returns %Rrc\n", rc));
7992 return rc;
7993}
7994
7995
7996VBOXDDU_DECL(int) VDAsyncWrite(PVBOXHDD pDisk, uint64_t uOffset, size_t cbWrite,
7997 PCRTSGSEG paSeg, unsigned cSeg,
7998 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
7999 void *pvUser1, void *pvUser2)
8000{
8001 int rc;
8002 int rc2;
8003 bool fLockWrite = false;
8004 PVDIOCTX pIoCtx = NULL;
8005
8006 LogFlowFunc(("pDisk=%#p uOffset=%llu paSeg=%p cSeg=%u cbWrite=%zu pvUser1=%#p pvUser2=%#p\n",
8007 pDisk, uOffset, paSeg, cSeg, cbWrite, pvUser1, pvUser2));
8008 do
8009 {
8010 /* sanity check */
8011 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8012 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8013
8014 /* Check arguments. */
8015 AssertMsgBreakStmt(cbWrite,
8016 ("cbWrite=%zu\n", cbWrite),
8017 rc = VERR_INVALID_PARAMETER);
8018 AssertMsgBreakStmt(VALID_PTR(paSeg),
8019 ("paSeg=%#p\n", paSeg),
8020 rc = VERR_INVALID_PARAMETER);
8021 AssertMsgBreakStmt(cSeg,
8022 ("cSeg=%zu\n", cSeg),
8023 rc = VERR_INVALID_PARAMETER);
8024
8025 rc2 = vdThreadStartWrite(pDisk);
8026 AssertRC(rc2);
8027 fLockWrite = true;
8028
8029 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
8030 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
8031 uOffset, cbWrite, pDisk->cbSize),
8032 rc = VERR_INVALID_PARAMETER);
8033
8034 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_WRITE, uOffset,
8035 cbWrite, paSeg, cSeg,
8036 pfnComplete, pvUser1, pvUser2,
8037 NULL, vdWriteHelperAsync);
8038 if (!pIoCtx)
8039 {
8040 rc = VERR_NO_MEMORY;
8041 break;
8042 }
8043
8044 PVDIMAGE pImage = pDisk->pLast;
8045 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
8046 pIoCtx->pImage = pImage;
8047
8048 rc = vdIoCtxProcess(pIoCtx);
8049 if (rc == VINF_VD_ASYNC_IO_FINISHED)
8050 {
8051 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
8052 vdIoCtxFree(pDisk, pIoCtx);
8053 else
8054 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
8055 }
8056 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
8057 vdIoCtxFree(pDisk, pIoCtx);
8058 } while (0);
8059
8060 if (RT_UNLIKELY(fLockWrite) && ( rc == VINF_VD_ASYNC_IO_FINISHED
8061 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
8062 {
8063 rc2 = vdThreadFinishWrite(pDisk);
8064 AssertRC(rc2);
8065 }
8066
8067 LogFlowFunc(("returns %Rrc\n", rc));
8068 return rc;
8069}
8070
8071
8072VBOXDDU_DECL(int) VDAsyncFlush(PVBOXHDD pDisk, PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
8073 void *pvUser1, void *pvUser2)
8074{
8075 int rc;
8076 int rc2;
8077 bool fLockWrite = false;
8078 PVDIOCTX pIoCtx = NULL;
8079
8080 LogFlowFunc(("pDisk=%#p\n", pDisk));
8081
8082 do
8083 {
8084 /* sanity check */
8085 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8086 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8087
8088 rc2 = vdThreadStartWrite(pDisk);
8089 AssertRC(rc2);
8090 fLockWrite = true;
8091
8092 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_FLUSH, 0,
8093 0, NULL, 0,
8094 pfnComplete, pvUser1, pvUser2,
8095 NULL, vdFlushHelperAsync);
8096 if (!pIoCtx)
8097 {
8098 rc = VERR_NO_MEMORY;
8099 break;
8100 }
8101
8102 PVDIMAGE pImage = pDisk->pLast;
8103 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
8104 pIoCtx->pImage = pImage;
8105
8106 rc = vdIoCtxProcess(pIoCtx);
8107 if (rc == VINF_VD_ASYNC_IO_FINISHED)
8108 {
8109 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
8110 vdIoCtxFree(pDisk, pIoCtx);
8111 else
8112 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
8113 }
8114 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
8115 vdIoCtxFree(pDisk, pIoCtx);
8116 } while (0);
8117
8118 if (RT_UNLIKELY(fLockWrite) && ( rc == VINF_VD_ASYNC_IO_FINISHED
8119 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
8120 {
8121 rc2 = vdThreadFinishWrite(pDisk);
8122 AssertRC(rc2);
8123 }
8124
8125 LogFlowFunc(("returns %Rrc\n", rc));
8126 return rc;
8127}
8128
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