VirtualBox

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

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

Added RTLdrLoadEx for exposing dlerror info.

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