VirtualBox

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

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

VD: Use S/G buffers instead of S/G arrays for VDAsyncWrite and VDAsyncRead (Preparation for moving the I/O cache in front of VD, see #5295)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 289.0 KB
Line 
1/* $Id: VD.cpp 34217 2010-11-21 14:45:14Z 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 = RTListNodeGetFirst(&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);
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);
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 = RTListNodeGetFirst(&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 = RTListNodeGetFirst(&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 * Initializes HDD backends.
3475 *
3476 * @returns VBox status code.
3477 */
3478VBOXDDU_DECL(int) VDInit(void)
3479{
3480 int rc = vdAddBackends(aStaticBackends, RT_ELEMENTS(aStaticBackends));
3481 if (RT_SUCCESS(rc))
3482 {
3483 rc = vdAddCacheBackends(aStaticCacheBackends, RT_ELEMENTS(aStaticCacheBackends));
3484 if (RT_SUCCESS(rc))
3485 {
3486 rc = vdLoadDynamicBackends();
3487 if (RT_SUCCESS(rc))
3488 rc = vdLoadDynamicCacheBackends();
3489 }
3490 }
3491 LogRel(("VDInit finished\n"));
3492 return rc;
3493}
3494
3495/**
3496 * Destroys loaded HDD backends.
3497 *
3498 * @returns VBox status code.
3499 */
3500VBOXDDU_DECL(int) VDShutdown(void)
3501{
3502 PVBOXHDDBACKEND *pBackends = g_apBackends;
3503 PVDCACHEBACKEND *pCacheBackends = g_apCacheBackends;
3504 unsigned cBackends = g_cBackends;
3505
3506 if (!pBackends)
3507 return VERR_INTERNAL_ERROR;
3508
3509 g_cBackends = 0;
3510 g_apBackends = NULL;
3511
3512#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
3513 for (unsigned i = 0; i < cBackends; i++)
3514 if (pBackends[i]->hPlugin != NIL_RTLDRMOD)
3515 RTLdrClose(pBackends[i]->hPlugin);
3516#endif
3517
3518 /* Clear the supported cache backends. */
3519 cBackends = g_cCacheBackends;
3520 g_cCacheBackends = 0;
3521 g_apCacheBackends = NULL;
3522
3523#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
3524 for (unsigned i = 0; i < cBackends; i++)
3525 if (pCacheBackends[i]->hPlugin != NIL_RTLDRMOD)
3526 RTLdrClose(pCacheBackends[i]->hPlugin);
3527#endif
3528
3529 if (pCacheBackends)
3530 RTMemFree(pCacheBackends);
3531 RTMemFree(pBackends);
3532 return VINF_SUCCESS;
3533}
3534
3535
3536/**
3537 * Lists all HDD backends and their capabilities in a caller-provided buffer.
3538 *
3539 * @returns VBox status code.
3540 * VERR_BUFFER_OVERFLOW if not enough space is passed.
3541 * @param cEntriesAlloc Number of list entries available.
3542 * @param pEntries Pointer to array for the entries.
3543 * @param pcEntriesUsed Number of entries returned.
3544 */
3545VBOXDDU_DECL(int) VDBackendInfo(unsigned cEntriesAlloc, PVDBACKENDINFO pEntries,
3546 unsigned *pcEntriesUsed)
3547{
3548 int rc = VINF_SUCCESS;
3549 PRTDIR pPluginDir = NULL;
3550 unsigned cEntries = 0;
3551
3552 LogFlowFunc(("cEntriesAlloc=%u pEntries=%#p pcEntriesUsed=%#p\n", cEntriesAlloc, pEntries, pcEntriesUsed));
3553 /* Check arguments. */
3554 AssertMsgReturn(cEntriesAlloc,
3555 ("cEntriesAlloc=%u\n", cEntriesAlloc),
3556 VERR_INVALID_PARAMETER);
3557 AssertMsgReturn(VALID_PTR(pEntries),
3558 ("pEntries=%#p\n", pEntries),
3559 VERR_INVALID_PARAMETER);
3560 AssertMsgReturn(VALID_PTR(pcEntriesUsed),
3561 ("pcEntriesUsed=%#p\n", pcEntriesUsed),
3562 VERR_INVALID_PARAMETER);
3563 if (!g_apBackends)
3564 VDInit();
3565
3566 if (cEntriesAlloc < g_cBackends)
3567 {
3568 *pcEntriesUsed = g_cBackends;
3569 return VERR_BUFFER_OVERFLOW;
3570 }
3571
3572 for (unsigned i = 0; i < g_cBackends; i++)
3573 {
3574 pEntries[i].pszBackend = g_apBackends[i]->pszBackendName;
3575 pEntries[i].uBackendCaps = g_apBackends[i]->uBackendCaps;
3576 pEntries[i].paFileExtensions = g_apBackends[i]->paFileExtensions;
3577 pEntries[i].paConfigInfo = g_apBackends[i]->paConfigInfo;
3578 pEntries[i].pfnComposeLocation = g_apBackends[i]->pfnComposeLocation;
3579 pEntries[i].pfnComposeName = g_apBackends[i]->pfnComposeName;
3580 }
3581
3582 LogFlowFunc(("returns %Rrc *pcEntriesUsed=%u\n", rc, cEntries));
3583 *pcEntriesUsed = g_cBackends;
3584 return rc;
3585}
3586
3587/**
3588 * Lists the capabilities of a backend identified by its name.
3589 *
3590 * @returns VBox status code.
3591 * @param pszBackend The backend name.
3592 * @param pEntries Pointer to an entry.
3593 */
3594VBOXDDU_DECL(int) VDBackendInfoOne(const char *pszBackend, PVDBACKENDINFO pEntry)
3595{
3596 LogFlowFunc(("pszBackend=%#p pEntry=%#p\n", pszBackend, pEntry));
3597 /* Check arguments. */
3598 AssertMsgReturn(VALID_PTR(pszBackend),
3599 ("pszBackend=%#p\n", pszBackend),
3600 VERR_INVALID_PARAMETER);
3601 AssertMsgReturn(VALID_PTR(pEntry),
3602 ("pEntry=%#p\n", pEntry),
3603 VERR_INVALID_PARAMETER);
3604 if (!g_apBackends)
3605 VDInit();
3606
3607 /* Go through loaded backends. */
3608 for (unsigned i = 0; i < g_cBackends; i++)
3609 {
3610 if (!RTStrICmp(pszBackend, g_apBackends[i]->pszBackendName))
3611 {
3612 pEntry->pszBackend = g_apBackends[i]->pszBackendName;
3613 pEntry->uBackendCaps = g_apBackends[i]->uBackendCaps;
3614 pEntry->paFileExtensions = g_apBackends[i]->paFileExtensions;
3615 pEntry->paConfigInfo = g_apBackends[i]->paConfigInfo;
3616 return VINF_SUCCESS;
3617 }
3618 }
3619
3620 return VERR_NOT_FOUND;
3621}
3622
3623/**
3624 * Allocates and initializes an empty HDD container.
3625 * No image files are opened.
3626 *
3627 * @returns VBox status code.
3628 * @param pVDIfsDisk Pointer to the per-disk VD interface list.
3629 * @param enmType Type of the image container.
3630 * @param ppDisk Where to store the reference to HDD container.
3631 */
3632VBOXDDU_DECL(int) VDCreate(PVDINTERFACE pVDIfsDisk, VDTYPE enmType, PVBOXHDD *ppDisk)
3633{
3634 int rc = VINF_SUCCESS;
3635 PVBOXHDD pDisk = NULL;
3636
3637 LogFlowFunc(("pVDIfsDisk=%#p\n", pVDIfsDisk));
3638 do
3639 {
3640 /* Check arguments. */
3641 AssertMsgBreakStmt(VALID_PTR(ppDisk),
3642 ("ppDisk=%#p\n", ppDisk),
3643 rc = VERR_INVALID_PARAMETER);
3644
3645 pDisk = (PVBOXHDD)RTMemAllocZ(sizeof(VBOXHDD));
3646 if (pDisk)
3647 {
3648 pDisk->u32Signature = VBOXHDDDISK_SIGNATURE;
3649 pDisk->enmType = enmType;
3650 pDisk->cImages = 0;
3651 pDisk->pBase = NULL;
3652 pDisk->pLast = NULL;
3653 pDisk->cbSize = 0;
3654 pDisk->PCHSGeometry.cCylinders = 0;
3655 pDisk->PCHSGeometry.cHeads = 0;
3656 pDisk->PCHSGeometry.cSectors = 0;
3657 pDisk->LCHSGeometry.cCylinders = 0;
3658 pDisk->LCHSGeometry.cHeads = 0;
3659 pDisk->LCHSGeometry.cSectors = 0;
3660 pDisk->pVDIfsDisk = pVDIfsDisk;
3661 pDisk->pInterfaceError = NULL;
3662 pDisk->pInterfaceErrorCallbacks = NULL;
3663 pDisk->pInterfaceThreadSync = NULL;
3664 pDisk->pInterfaceThreadSyncCallbacks = NULL;
3665 pDisk->fLocked = false;
3666 RTListInit(&pDisk->ListWriteLocked);
3667
3668 /* Create the I/O ctx cache */
3669 rc = RTMemCacheCreate(&pDisk->hMemCacheIoCtx, sizeof(VDIOCTX), 0, UINT32_MAX,
3670 NULL, NULL, NULL, 0);
3671 if (RT_FAILURE(rc))
3672 {
3673 RTMemFree(pDisk);
3674 break;
3675 }
3676
3677 /* Create the I/O task cache */
3678 rc = RTMemCacheCreate(&pDisk->hMemCacheIoTask, sizeof(VDIOTASK), 0, UINT32_MAX,
3679 NULL, NULL, NULL, 0);
3680 if (RT_FAILURE(rc))
3681 {
3682 RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
3683 RTMemFree(pDisk);
3684 break;
3685 }
3686
3687 /* Create critical section. */
3688 rc = RTCritSectInit(&pDisk->CritSect);
3689 if (RT_FAILURE(rc))
3690 {
3691 RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
3692 RTMemCacheDestroy(pDisk->hMemCacheIoTask);
3693 RTMemFree(pDisk);
3694 break;
3695 }
3696
3697 pDisk->pInterfaceError = VDInterfaceGet(pVDIfsDisk, VDINTERFACETYPE_ERROR);
3698 if (pDisk->pInterfaceError)
3699 pDisk->pInterfaceErrorCallbacks = VDGetInterfaceError(pDisk->pInterfaceError);
3700
3701 pDisk->pInterfaceThreadSync = VDInterfaceGet(pVDIfsDisk, VDINTERFACETYPE_THREADSYNC);
3702 if (pDisk->pInterfaceThreadSync)
3703 pDisk->pInterfaceThreadSyncCallbacks = VDGetInterfaceThreadSync(pDisk->pInterfaceThreadSync);
3704
3705 /* Create fallback I/O callback table */
3706 pDisk->VDIIOCallbacks.cbSize = sizeof(VDINTERFACEIO);
3707 pDisk->VDIIOCallbacks.enmInterface = VDINTERFACETYPE_IO;
3708 pDisk->VDIIOCallbacks.pfnOpen = vdIOOpenFallback;
3709 pDisk->VDIIOCallbacks.pfnClose = vdIOCloseFallback;
3710 pDisk->VDIIOCallbacks.pfnDelete = vdIODeleteFallback;
3711 pDisk->VDIIOCallbacks.pfnMove = vdIOMoveFallback;
3712 pDisk->VDIIOCallbacks.pfnGetFreeSpace = vdIOGetFreeSpaceFallback;
3713 pDisk->VDIIOCallbacks.pfnGetModificationTime = vdIOGetModificationTimeFallback;
3714 pDisk->VDIIOCallbacks.pfnGetSize = vdIOGetSizeFallback;
3715 pDisk->VDIIOCallbacks.pfnSetSize = vdIOSetSizeFallback;
3716 pDisk->VDIIOCallbacks.pfnReadSync = vdIOReadSyncFallback;
3717 pDisk->VDIIOCallbacks.pfnWriteSync = vdIOWriteSyncFallback;
3718 pDisk->VDIIOCallbacks.pfnFlushSync = vdIOFlushSyncFallback;
3719 pDisk->VDIIOCallbacks.pfnReadAsync = vdIOReadAsyncFallback;
3720 pDisk->VDIIOCallbacks.pfnWriteAsync = vdIOWriteAsyncFallback;
3721 pDisk->VDIIOCallbacks.pfnFlushAsync = vdIOFlushAsyncFallback;
3722
3723 /*
3724 * Create the internal I/O callback table.
3725 * The interface is per-image but no need to duplicate the
3726 * callback table every time.
3727 */
3728 pDisk->VDIIOIntCallbacks.cbSize = sizeof(VDINTERFACEIOINT);
3729 pDisk->VDIIOIntCallbacks.enmInterface = VDINTERFACETYPE_IOINT;
3730 pDisk->VDIIOIntCallbacks.pfnOpen = vdIOIntOpen;
3731 pDisk->VDIIOIntCallbacks.pfnClose = vdIOIntClose;
3732 pDisk->VDIIOIntCallbacks.pfnDelete = vdIOIntDelete;
3733 pDisk->VDIIOIntCallbacks.pfnMove = vdIOIntMove;
3734 pDisk->VDIIOIntCallbacks.pfnGetFreeSpace = vdIOIntGetFreeSpace;
3735 pDisk->VDIIOIntCallbacks.pfnGetModificationTime = vdIOIntGetModificationTime;
3736 pDisk->VDIIOIntCallbacks.pfnGetSize = vdIOIntGetSize;
3737 pDisk->VDIIOIntCallbacks.pfnSetSize = vdIOIntSetSize;
3738 pDisk->VDIIOIntCallbacks.pfnReadSync = vdIOIntReadSync;
3739 pDisk->VDIIOIntCallbacks.pfnWriteSync = vdIOIntWriteSync;
3740 pDisk->VDIIOIntCallbacks.pfnFlushSync = vdIOIntFlushSync;
3741 pDisk->VDIIOIntCallbacks.pfnReadUserAsync = vdIOIntReadUserAsync;
3742 pDisk->VDIIOIntCallbacks.pfnWriteUserAsync = vdIOIntWriteUserAsync;
3743 pDisk->VDIIOIntCallbacks.pfnReadMetaAsync = vdIOIntReadMetaAsync;
3744 pDisk->VDIIOIntCallbacks.pfnWriteMetaAsync = vdIOIntWriteMetaAsync;
3745 pDisk->VDIIOIntCallbacks.pfnMetaXferRelease = vdIOIntMetaXferRelease;
3746 pDisk->VDIIOIntCallbacks.pfnFlushAsync = vdIOIntFlushAsync;
3747 pDisk->VDIIOIntCallbacks.pfnIoCtxCopyFrom = vdIOIntIoCtxCopyFrom;
3748 pDisk->VDIIOIntCallbacks.pfnIoCtxCopyTo = vdIOIntIoCtxCopyTo;
3749 pDisk->VDIIOIntCallbacks.pfnIoCtxSet = vdIOIntIoCtxSet;
3750 pDisk->VDIIOIntCallbacks.pfnIoCtxSegArrayCreate = vdIOIntIoCtxSegArrayCreate;
3751 pDisk->VDIIOIntCallbacks.pfnIoCtxCompleted = vdIOIntIoCtxCompleted;
3752
3753 *ppDisk = pDisk;
3754 }
3755 else
3756 {
3757 rc = VERR_NO_MEMORY;
3758 break;
3759 }
3760 } while (0);
3761
3762 LogFlowFunc(("returns %Rrc (pDisk=%#p)\n", rc, pDisk));
3763 return rc;
3764}
3765
3766/**
3767 * Destroys HDD container.
3768 * If container has opened image files they will be closed.
3769 *
3770 * @param pDisk Pointer to HDD container.
3771 */
3772VBOXDDU_DECL(void) VDDestroy(PVBOXHDD pDisk)
3773{
3774 LogFlowFunc(("pDisk=%#p\n", pDisk));
3775 do
3776 {
3777 /* sanity check */
3778 AssertPtrBreak(pDisk);
3779 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3780 VDCloseAll(pDisk);
3781 RTCritSectDelete(&pDisk->CritSect);
3782 RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
3783 RTMemCacheDestroy(pDisk->hMemCacheIoTask);
3784 RTMemFree(pDisk);
3785 } while (0);
3786 LogFlowFunc(("returns\n"));
3787}
3788
3789/**
3790 * Try to get the backend name which can use this image.
3791 *
3792 * @returns VBox status code.
3793 * VINF_SUCCESS if a plugin was found.
3794 * ppszFormat contains the string which can be used as backend name.
3795 * VERR_NOT_SUPPORTED if no backend was found.
3796 * @param pVDIfsDisk Pointer to the per-disk VD interface list.
3797 * @param pVDIfsImage Pointer to the per-image VD interface list.
3798 * @param pszFilename Name of the image file for which the backend is queried.
3799 * @param ppszFormat Receives pointer of the UTF-8 string which contains the format name.
3800 * The returned pointer must be freed using RTStrFree().
3801 */
3802VBOXDDU_DECL(int) VDGetFormat(PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
3803 const char *pszFilename, char **ppszFormat, VDTYPE *penmType)
3804{
3805 int rc = VERR_NOT_SUPPORTED;
3806 VDINTERFACEIOINT VDIIOIntCallbacks;
3807 VDINTERFACE VDIIOInt;
3808 VDINTERFACEIO VDIIOCallbacksFallback;
3809 PVDINTERFACE pInterfaceIO;
3810 PVDINTERFACEIO pInterfaceIOCallbacks;
3811
3812 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
3813 /* Check arguments. */
3814 AssertMsgReturn(VALID_PTR(pszFilename) && *pszFilename,
3815 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
3816 VERR_INVALID_PARAMETER);
3817 AssertMsgReturn(VALID_PTR(ppszFormat),
3818 ("ppszFormat=%#p\n", ppszFormat),
3819 VERR_INVALID_PARAMETER);
3820 AssertMsgReturn(VALID_PTR(ppszFormat),
3821 ("penmType=%#p\n", penmType),
3822 VERR_INVALID_PARAMETER);
3823
3824 if (!g_apBackends)
3825 VDInit();
3826
3827 pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
3828 if (!pInterfaceIO)
3829 {
3830 /*
3831 * Caller doesn't provide an I/O interface, create our own using the
3832 * native file API.
3833 */
3834 VDIIOCallbacksFallback.cbSize = sizeof(VDINTERFACEIO);
3835 VDIIOCallbacksFallback.enmInterface = VDINTERFACETYPE_IO;
3836 VDIIOCallbacksFallback.pfnOpen = vdIOOpenFallback;
3837 VDIIOCallbacksFallback.pfnClose = vdIOCloseFallback;
3838 VDIIOCallbacksFallback.pfnDelete = vdIODeleteFallback;
3839 VDIIOCallbacksFallback.pfnMove = vdIOMoveFallback;
3840 VDIIOCallbacksFallback.pfnGetFreeSpace = vdIOGetFreeSpaceFallback;
3841 VDIIOCallbacksFallback.pfnGetModificationTime = vdIOGetModificationTimeFallback;
3842 VDIIOCallbacksFallback.pfnGetSize = vdIOGetSizeFallback;
3843 VDIIOCallbacksFallback.pfnSetSize = vdIOSetSizeFallback;
3844 VDIIOCallbacksFallback.pfnReadSync = vdIOReadSyncFallback;
3845 VDIIOCallbacksFallback.pfnWriteSync = vdIOWriteSyncFallback;
3846 VDIIOCallbacksFallback.pfnFlushSync = vdIOFlushSyncFallback;
3847 pInterfaceIOCallbacks = &VDIIOCallbacksFallback;
3848 }
3849 else
3850 pInterfaceIOCallbacks = VDGetInterfaceIO(pInterfaceIO);
3851
3852 /* Set up the internal I/O interface. */
3853 AssertReturn(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
3854 VERR_INVALID_PARAMETER);
3855 VDIIOIntCallbacks.cbSize = sizeof(VDINTERFACEIOINT);
3856 VDIIOIntCallbacks.enmInterface = VDINTERFACETYPE_IOINT;
3857 VDIIOIntCallbacks.pfnOpen = vdIOIntOpenLimited;
3858 VDIIOIntCallbacks.pfnClose = vdIOIntCloseLimited;
3859 VDIIOIntCallbacks.pfnDelete = vdIOIntDeleteLimited;
3860 VDIIOIntCallbacks.pfnMove = vdIOIntMoveLimited;
3861 VDIIOIntCallbacks.pfnGetFreeSpace = vdIOIntGetFreeSpaceLimited;
3862 VDIIOIntCallbacks.pfnGetModificationTime = vdIOIntGetModificationTimeLimited;
3863 VDIIOIntCallbacks.pfnGetSize = vdIOIntGetSizeLimited;
3864 VDIIOIntCallbacks.pfnSetSize = vdIOIntSetSizeLimited;
3865 VDIIOIntCallbacks.pfnReadSync = vdIOIntReadSyncLimited;
3866 VDIIOIntCallbacks.pfnWriteSync = vdIOIntWriteSyncLimited;
3867 VDIIOIntCallbacks.pfnFlushSync = vdIOIntFlushSyncLimited;
3868 VDIIOIntCallbacks.pfnReadUserAsync = NULL;
3869 VDIIOIntCallbacks.pfnWriteUserAsync = NULL;
3870 VDIIOIntCallbacks.pfnReadMetaAsync = NULL;
3871 VDIIOIntCallbacks.pfnWriteMetaAsync = NULL;
3872 VDIIOIntCallbacks.pfnFlushAsync = NULL;
3873 rc = VDInterfaceAdd(&VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
3874 &VDIIOIntCallbacks, pInterfaceIOCallbacks, &pVDIfsImage);
3875 AssertRC(rc);
3876
3877 /* Find the backend supporting this file format. */
3878 for (unsigned i = 0; i < g_cBackends; i++)
3879 {
3880 if (g_apBackends[i]->pfnCheckIfValid)
3881 {
3882 rc = g_apBackends[i]->pfnCheckIfValid(pszFilename, pVDIfsDisk,
3883 pVDIfsImage, penmType);
3884 if ( RT_SUCCESS(rc)
3885 /* The correct backend has been found, but there is a small
3886 * incompatibility so that the file cannot be used. Stop here
3887 * and signal success - the actual open will of course fail,
3888 * but that will create a really sensible error message. */
3889 || ( rc != VERR_VD_GEN_INVALID_HEADER
3890 && rc != VERR_VD_VDI_INVALID_HEADER
3891 && rc != VERR_VD_VMDK_INVALID_HEADER
3892 && rc != VERR_VD_ISCSI_INVALID_HEADER
3893 && rc != VERR_VD_VHD_INVALID_HEADER
3894 && rc != VERR_VD_RAW_INVALID_HEADER
3895 && rc != VERR_VD_PARALLELS_INVALID_HEADER
3896 && rc != VERR_VD_DMG_INVALID_HEADER))
3897 {
3898 /* Copy the name into the new string. */
3899 char *pszFormat = RTStrDup(g_apBackends[i]->pszBackendName);
3900 if (!pszFormat)
3901 {
3902 rc = VERR_NO_MEMORY;
3903 break;
3904 }
3905 *ppszFormat = pszFormat;
3906 rc = VINF_SUCCESS;
3907 break;
3908 }
3909 rc = VERR_NOT_SUPPORTED;
3910 }
3911 }
3912
3913 /* Try the cache backends. */
3914 if (rc == VERR_NOT_SUPPORTED)
3915 {
3916 for (unsigned i = 0; i < g_cCacheBackends; i++)
3917 {
3918 if (g_apCacheBackends[i]->pfnProbe)
3919 {
3920 rc = g_apCacheBackends[i]->pfnProbe(pszFilename, pVDIfsDisk,
3921 pVDIfsImage);
3922 if ( RT_SUCCESS(rc)
3923 || (rc != VERR_VD_GEN_INVALID_HEADER))
3924 {
3925 /* Copy the name into the new string. */
3926 char *pszFormat = RTStrDup(g_apBackends[i]->pszBackendName);
3927 if (!pszFormat)
3928 {
3929 rc = VERR_NO_MEMORY;
3930 break;
3931 }
3932 *ppszFormat = pszFormat;
3933 rc = VINF_SUCCESS;
3934 break;
3935 }
3936 rc = VERR_NOT_SUPPORTED;
3937 }
3938 }
3939 }
3940
3941 LogFlowFunc(("returns %Rrc *ppszFormat=\"%s\"\n", rc, *ppszFormat));
3942 return rc;
3943}
3944
3945/**
3946 * Opens an image file.
3947 *
3948 * The first opened image file in HDD container must have a base image type,
3949 * others (next opened images) must be a differencing or undo images.
3950 * Linkage is checked for differencing image to be in consistence with the previously opened image.
3951 * When another differencing image is opened and the last image was opened in read/write access
3952 * mode, then the last image is reopened in read-only with deny write sharing mode. This allows
3953 * other processes to use images in read-only mode too.
3954 *
3955 * Note that the image is opened in read-only mode if a read/write open is not possible.
3956 * Use VDIsReadOnly to check open mode.
3957 *
3958 * @returns VBox status code.
3959 * @param pDisk Pointer to HDD container.
3960 * @param pszBackend Name of the image file backend to use.
3961 * @param pszFilename Name of the image file to open.
3962 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
3963 * @param pVDIfsImage Pointer to the per-image VD interface list.
3964 */
3965VBOXDDU_DECL(int) VDOpen(PVBOXHDD pDisk, const char *pszBackend,
3966 const char *pszFilename, unsigned uOpenFlags,
3967 PVDINTERFACE pVDIfsImage)
3968{
3969 int rc = VINF_SUCCESS;
3970 int rc2;
3971 bool fLockWrite = false;
3972 PVDIMAGE pImage = NULL;
3973
3974 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x, pVDIfsImage=%#p\n",
3975 pDisk, pszBackend, pszFilename, uOpenFlags, pVDIfsImage));
3976
3977 do
3978 {
3979 /* sanity check */
3980 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3981 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3982
3983 /* Check arguments. */
3984 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
3985 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
3986 rc = VERR_INVALID_PARAMETER);
3987 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
3988 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
3989 rc = VERR_INVALID_PARAMETER);
3990 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
3991 ("uOpenFlags=%#x\n", uOpenFlags),
3992 rc = VERR_INVALID_PARAMETER);
3993
3994 /* Set up image descriptor. */
3995 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
3996 if (!pImage)
3997 {
3998 rc = VERR_NO_MEMORY;
3999 break;
4000 }
4001 pImage->pszFilename = RTStrDup(pszFilename);
4002 if (!pImage->pszFilename)
4003 {
4004 rc = VERR_NO_MEMORY;
4005 break;
4006 }
4007
4008 pImage->VDIo.pDisk = pDisk;
4009 pImage->pVDIfsImage = pVDIfsImage;
4010
4011 rc = vdFindBackend(pszBackend, &pImage->Backend);
4012 if (RT_FAILURE(rc))
4013 break;
4014 if (!pImage->Backend)
4015 {
4016 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4017 N_("VD: unknown backend name '%s'"), pszBackend);
4018 break;
4019 }
4020
4021 /* Set up the I/O interface. */
4022 pImage->VDIo.pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
4023 if (pImage->VDIo.pInterfaceIO)
4024 pImage->VDIo.pInterfaceIOCallbacks = VDGetInterfaceIO(pImage->VDIo.pInterfaceIO);
4025 else
4026 {
4027 rc = VDInterfaceAdd(&pImage->VDIo.VDIIO, "VD_IO", VDINTERFACETYPE_IO,
4028 &pDisk->VDIIOCallbacks, pDisk, &pVDIfsImage);
4029 pImage->VDIo.pInterfaceIO = &pImage->VDIo.VDIIO;
4030 pImage->VDIo.pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
4031 }
4032
4033 /* Set up the internal I/O interface. */
4034 AssertBreakStmt(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
4035 rc = VERR_INVALID_PARAMETER);
4036 rc = VDInterfaceAdd(&pImage->VDIo.VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
4037 &pDisk->VDIIOIntCallbacks, &pImage->VDIo, &pImage->pVDIfsImage);
4038 AssertRC(rc);
4039
4040 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
4041 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
4042 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
4043 pDisk->pVDIfsDisk,
4044 pImage->pVDIfsImage,
4045 pDisk->enmType,
4046 &pImage->pBackendData);
4047 /* If the open in read-write mode failed, retry in read-only mode. */
4048 if (RT_FAILURE(rc))
4049 {
4050 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
4051 && ( rc == VERR_ACCESS_DENIED
4052 || rc == VERR_PERMISSION_DENIED
4053 || rc == VERR_WRITE_PROTECT
4054 || rc == VERR_SHARING_VIOLATION
4055 || rc == VERR_FILE_LOCK_FAILED))
4056 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
4057 (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
4058 | VD_OPEN_FLAGS_READONLY,
4059 pDisk->pVDIfsDisk,
4060 pImage->pVDIfsImage,
4061 pDisk->enmType,
4062 &pImage->pBackendData);
4063 if (RT_FAILURE(rc))
4064 {
4065 rc = vdError(pDisk, rc, RT_SRC_POS,
4066 N_("VD: error %Rrc opening image file '%s'"), rc, pszFilename);
4067 break;
4068 }
4069 }
4070
4071 /* Lock disk for writing, as we modify pDisk information below. */
4072 rc2 = vdThreadStartWrite(pDisk);
4073 AssertRC(rc2);
4074 fLockWrite = true;
4075
4076 pImage->VDIo.pBackendData = pImage->pBackendData;
4077
4078 /* Check image type. As the image itself has only partial knowledge
4079 * whether it's a base image or not, this info is derived here. The
4080 * base image can be fixed or normal, all others must be normal or
4081 * diff images. Some image formats don't distinguish between normal
4082 * and diff images, so this must be corrected here. */
4083 unsigned uImageFlags;
4084 uImageFlags = pImage->Backend->pfnGetImageFlags(pImage->pBackendData);
4085 if (RT_FAILURE(rc))
4086 uImageFlags = VD_IMAGE_FLAGS_NONE;
4087 if ( RT_SUCCESS(rc)
4088 && !(uOpenFlags & VD_OPEN_FLAGS_INFO))
4089 {
4090 if ( pDisk->cImages == 0
4091 && (uImageFlags & VD_IMAGE_FLAGS_DIFF))
4092 {
4093 rc = VERR_VD_INVALID_TYPE;
4094 break;
4095 }
4096 else if (pDisk->cImages != 0)
4097 {
4098 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
4099 {
4100 rc = VERR_VD_INVALID_TYPE;
4101 break;
4102 }
4103 else
4104 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
4105 }
4106 }
4107
4108 /* Ensure we always get correct diff information, even if the backend
4109 * doesn't actually have a stored flag for this. It must not return
4110 * bogus information for the parent UUID if it is not a diff image. */
4111 RTUUID parentUuid;
4112 RTUuidClear(&parentUuid);
4113 rc2 = pImage->Backend->pfnGetParentUuid(pImage->pBackendData, &parentUuid);
4114 if (RT_SUCCESS(rc2) && !RTUuidIsNull(&parentUuid))
4115 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
4116
4117 pImage->uImageFlags = uImageFlags;
4118
4119 /* Force sane optimization settings. It's not worth avoiding writes
4120 * to fixed size images. The overhead would have almost no payback. */
4121 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
4122 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
4123
4124 /** @todo optionally check UUIDs */
4125
4126 /* Cache disk information. */
4127 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
4128
4129 /* Cache PCHS geometry. */
4130 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
4131 &pDisk->PCHSGeometry);
4132 if (RT_FAILURE(rc2))
4133 {
4134 pDisk->PCHSGeometry.cCylinders = 0;
4135 pDisk->PCHSGeometry.cHeads = 0;
4136 pDisk->PCHSGeometry.cSectors = 0;
4137 }
4138 else
4139 {
4140 /* Make sure the PCHS geometry is properly clipped. */
4141 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
4142 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
4143 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
4144 }
4145
4146 /* Cache LCHS geometry. */
4147 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
4148 &pDisk->LCHSGeometry);
4149 if (RT_FAILURE(rc2))
4150 {
4151 pDisk->LCHSGeometry.cCylinders = 0;
4152 pDisk->LCHSGeometry.cHeads = 0;
4153 pDisk->LCHSGeometry.cSectors = 0;
4154 }
4155 else
4156 {
4157 /* Make sure the LCHS geometry is properly clipped. */
4158 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
4159 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
4160 }
4161
4162 if (pDisk->cImages != 0)
4163 {
4164 /* Switch previous image to read-only mode. */
4165 unsigned uOpenFlagsPrevImg;
4166 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
4167 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
4168 {
4169 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
4170 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pBackendData, uOpenFlagsPrevImg);
4171 }
4172 }
4173
4174 if (RT_SUCCESS(rc))
4175 {
4176 /* Image successfully opened, make it the last image. */
4177 vdAddImageToList(pDisk, pImage);
4178 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
4179 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
4180 }
4181 else
4182 {
4183 /* Error detected, but image opened. Close image. */
4184 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, false);
4185 AssertRC(rc2);
4186 pImage->pBackendData = NULL;
4187 }
4188 } while (0);
4189
4190 if (RT_UNLIKELY(fLockWrite))
4191 {
4192 rc2 = vdThreadFinishWrite(pDisk);
4193 AssertRC(rc2);
4194 }
4195
4196 if (RT_FAILURE(rc))
4197 {
4198 if (pImage)
4199 {
4200 if (pImage->pszFilename)
4201 RTStrFree(pImage->pszFilename);
4202 RTMemFree(pImage);
4203 }
4204 }
4205
4206 LogFlowFunc(("returns %Rrc\n", rc));
4207 return rc;
4208}
4209
4210/**
4211 * Opens a cache image.
4212 *
4213 * @return VBox status code.
4214 * @param pDisk Pointer to the HDD container which should use the cache image.
4215 * @param pszBackend Name of the cache file backend to use (case insensitive).
4216 * @param pszFilename Name of the cache image to open.
4217 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
4218 * @param pVDIfsCache Pointer to the per-cache VD interface list.
4219 */
4220VBOXDDU_DECL(int) VDCacheOpen(PVBOXHDD pDisk, const char *pszBackend,
4221 const char *pszFilename, unsigned uOpenFlags,
4222 PVDINTERFACE pVDIfsCache)
4223{
4224 int rc = VINF_SUCCESS;
4225 int rc2;
4226 bool fLockWrite = false;
4227 PVDCACHE pCache = NULL;
4228
4229 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x, pVDIfsCache=%#p\n",
4230 pDisk, pszBackend, pszFilename, uOpenFlags, pVDIfsCache));
4231
4232 do
4233 {
4234 /* sanity check */
4235 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4236 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4237
4238 /* Check arguments. */
4239 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
4240 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
4241 rc = VERR_INVALID_PARAMETER);
4242 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
4243 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
4244 rc = VERR_INVALID_PARAMETER);
4245 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
4246 ("uOpenFlags=%#x\n", uOpenFlags),
4247 rc = VERR_INVALID_PARAMETER);
4248
4249 /* Set up image descriptor. */
4250 pCache = (PVDCACHE)RTMemAllocZ(sizeof(VDCACHE));
4251 if (!pCache)
4252 {
4253 rc = VERR_NO_MEMORY;
4254 break;
4255 }
4256 pCache->pszFilename = RTStrDup(pszFilename);
4257 if (!pCache->pszFilename)
4258 {
4259 rc = VERR_NO_MEMORY;
4260 break;
4261 }
4262
4263 pCache->VDIo.pDisk = pDisk;
4264 pCache->pVDIfsCache = pVDIfsCache;
4265
4266 rc = vdFindCacheBackend(pszBackend, &pCache->Backend);
4267 if (RT_FAILURE(rc))
4268 break;
4269 if (!pCache->Backend)
4270 {
4271 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4272 N_("VD: unknown backend name '%s'"), pszBackend);
4273 break;
4274 }
4275
4276 /* Set up the I/O interface. */
4277 pCache->VDIo.pInterfaceIO = VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IO);
4278 if (pCache->VDIo.pInterfaceIO)
4279 pCache->VDIo.pInterfaceIOCallbacks = VDGetInterfaceIO(pCache->VDIo.pInterfaceIO);
4280 else
4281 {
4282 rc = VDInterfaceAdd(&pCache->VDIo.VDIIO, "VD_IO", VDINTERFACETYPE_IO,
4283 &pDisk->VDIIOCallbacks, pDisk, &pVDIfsCache);
4284 pCache->VDIo.pInterfaceIO = &pCache->VDIo.VDIIO;
4285 pCache->VDIo.pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
4286 }
4287
4288 /* Set up the internal I/O interface. */
4289 AssertBreakStmt(!VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IOINT),
4290 rc = VERR_INVALID_PARAMETER);
4291 rc = VDInterfaceAdd(&pCache->VDIo.VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
4292 &pDisk->VDIIOIntCallbacks, &pCache->VDIo, &pCache->pVDIfsCache);
4293 AssertRC(rc);
4294
4295 pCache->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
4296 rc = pCache->Backend->pfnOpen(pCache->pszFilename,
4297 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
4298 pDisk->pVDIfsDisk,
4299 pCache->pVDIfsCache,
4300 &pCache->pBackendData);
4301 /* If the open in read-write mode failed, retry in read-only mode. */
4302 if (RT_FAILURE(rc))
4303 {
4304 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
4305 && ( rc == VERR_ACCESS_DENIED
4306 || rc == VERR_PERMISSION_DENIED
4307 || rc == VERR_WRITE_PROTECT
4308 || rc == VERR_SHARING_VIOLATION
4309 || rc == VERR_FILE_LOCK_FAILED))
4310 rc = pCache->Backend->pfnOpen(pCache->pszFilename,
4311 (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
4312 | VD_OPEN_FLAGS_READONLY,
4313 pDisk->pVDIfsDisk,
4314 pCache->pVDIfsCache,
4315 &pCache->pBackendData);
4316 if (RT_FAILURE(rc))
4317 {
4318 rc = vdError(pDisk, rc, RT_SRC_POS,
4319 N_("VD: error %Rrc opening image file '%s'"), rc, pszFilename);
4320 break;
4321 }
4322 }
4323
4324 /* Lock disk for writing, as we modify pDisk information below. */
4325 rc2 = vdThreadStartWrite(pDisk);
4326 AssertRC(rc2);
4327 fLockWrite = true;
4328
4329 /*
4330 * Check that the modification UUID of the cache and last image
4331 * match. If not the image was modified in-between without the cache.
4332 * The cache might contain stale data.
4333 */
4334 RTUUID UuidImage, UuidCache;
4335
4336 rc = pCache->Backend->pfnGetModificationUuid(pCache->pBackendData,
4337 &UuidCache);
4338 if (RT_SUCCESS(rc))
4339 {
4340 rc = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
4341 &UuidImage);
4342 if (RT_SUCCESS(rc))
4343 {
4344 if (RTUuidCompare(&UuidImage, &UuidCache))
4345 rc = VERR_VD_CACHE_NOT_UP_TO_DATE;
4346 }
4347 }
4348
4349 /*
4350 * We assume that the user knows what he is doing if one of the images
4351 * doesn't support the modification uuid.
4352 */
4353 if (rc == VERR_NOT_SUPPORTED)
4354 rc = VINF_SUCCESS;
4355
4356 if (RT_SUCCESS(rc))
4357 {
4358 /* Cache successfully opened, make it the current one. */
4359 if (!pDisk->pCache)
4360 pDisk->pCache = pCache;
4361 else
4362 rc = VERR_VD_CACHE_ALREADY_EXISTS;
4363 }
4364
4365 if (RT_FAILURE(rc))
4366 {
4367 /* Error detected, but image opened. Close image. */
4368 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, false);
4369 AssertRC(rc2);
4370 pCache->pBackendData = NULL;
4371 }
4372 } while (0);
4373
4374 if (RT_UNLIKELY(fLockWrite))
4375 {
4376 rc2 = vdThreadFinishWrite(pDisk);
4377 AssertRC(rc2);
4378 }
4379
4380 if (RT_FAILURE(rc))
4381 {
4382 if (pCache)
4383 {
4384 if (pCache->pszFilename)
4385 RTStrFree(pCache->pszFilename);
4386 RTMemFree(pCache);
4387 }
4388 }
4389
4390 LogFlowFunc(("returns %Rrc\n", rc));
4391 return rc;
4392}
4393
4394/**
4395 * Creates and opens a new base image file.
4396 *
4397 * @returns VBox status code.
4398 * @param pDisk Pointer to HDD container.
4399 * @param pszBackend Name of the image file backend to use.
4400 * @param pszFilename Name of the image file to create.
4401 * @param cbSize Image size in bytes.
4402 * @param uImageFlags Flags specifying special image features.
4403 * @param pszComment Pointer to image comment. NULL is ok.
4404 * @param pPCHSGeometry Pointer to physical disk geometry <= (16383,16,63). Not NULL.
4405 * @param pLCHSGeometry Pointer to logical disk geometry <= (x,255,63). Not NULL.
4406 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
4407 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
4408 * @param pVDIfsImage Pointer to the per-image VD interface list.
4409 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
4410 */
4411VBOXDDU_DECL(int) VDCreateBase(PVBOXHDD pDisk, const char *pszBackend,
4412 const char *pszFilename, uint64_t cbSize,
4413 unsigned uImageFlags, const char *pszComment,
4414 PCVDGEOMETRY pPCHSGeometry,
4415 PCVDGEOMETRY pLCHSGeometry,
4416 PCRTUUID pUuid, unsigned uOpenFlags,
4417 PVDINTERFACE pVDIfsImage,
4418 PVDINTERFACE pVDIfsOperation)
4419{
4420 int rc = VINF_SUCCESS;
4421 int rc2;
4422 bool fLockWrite = false, fLockRead = false;
4423 PVDIMAGE pImage = NULL;
4424 RTUUID uuid;
4425
4426 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",
4427 pDisk, pszBackend, pszFilename, cbSize, uImageFlags, pszComment,
4428 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
4429 pPCHSGeometry->cSectors, pLCHSGeometry->cCylinders,
4430 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors, pUuid,
4431 uOpenFlags, pVDIfsImage, pVDIfsOperation));
4432
4433 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
4434 VDINTERFACETYPE_PROGRESS);
4435 PVDINTERFACEPROGRESS pCbProgress = NULL;
4436 if (pIfProgress)
4437 pCbProgress = VDGetInterfaceProgress(pIfProgress);
4438
4439 do
4440 {
4441 /* sanity check */
4442 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4443 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4444
4445 /* Check arguments. */
4446 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
4447 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
4448 rc = VERR_INVALID_PARAMETER);
4449 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
4450 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
4451 rc = VERR_INVALID_PARAMETER);
4452 AssertMsgBreakStmt(cbSize,
4453 ("cbSize=%llu\n", cbSize),
4454 rc = VERR_INVALID_PARAMETER);
4455 AssertMsgBreakStmt( ((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0)
4456 || ((uImageFlags & (VD_IMAGE_FLAGS_FIXED | VD_IMAGE_FLAGS_DIFF)) != VD_IMAGE_FLAGS_FIXED),
4457 ("uImageFlags=%#x\n", uImageFlags),
4458 rc = VERR_INVALID_PARAMETER);
4459 /* The PCHS geometry fields may be 0 to leave it for later. */
4460 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
4461 && pPCHSGeometry->cHeads <= 16
4462 && pPCHSGeometry->cSectors <= 63,
4463 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
4464 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
4465 pPCHSGeometry->cSectors),
4466 rc = VERR_INVALID_PARAMETER);
4467 /* The LCHS geometry fields may be 0 to leave it to later autodetection. */
4468 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
4469 && pLCHSGeometry->cHeads <= 255
4470 && pLCHSGeometry->cSectors <= 63,
4471 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
4472 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
4473 pLCHSGeometry->cSectors),
4474 rc = VERR_INVALID_PARAMETER);
4475 /* The UUID may be NULL. */
4476 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
4477 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
4478 rc = VERR_INVALID_PARAMETER);
4479 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
4480 ("uOpenFlags=%#x\n", uOpenFlags),
4481 rc = VERR_INVALID_PARAMETER);
4482
4483 /* Check state. Needs a temporary read lock. Holding the write lock
4484 * all the time would be blocking other activities for too long. */
4485 rc2 = vdThreadStartRead(pDisk);
4486 AssertRC(rc2);
4487 fLockRead = true;
4488 AssertMsgBreakStmt(pDisk->cImages == 0,
4489 ("Create base image cannot be done with other images open\n"),
4490 rc = VERR_VD_INVALID_STATE);
4491 rc2 = vdThreadFinishRead(pDisk);
4492 AssertRC(rc2);
4493 fLockRead = false;
4494
4495 /* Set up image descriptor. */
4496 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
4497 if (!pImage)
4498 {
4499 rc = VERR_NO_MEMORY;
4500 break;
4501 }
4502 pImage->pszFilename = RTStrDup(pszFilename);
4503 if (!pImage->pszFilename)
4504 {
4505 rc = VERR_NO_MEMORY;
4506 break;
4507 }
4508 pImage->VDIo.pDisk = pDisk;
4509 pImage->pVDIfsImage = pVDIfsImage;
4510
4511 /* Set up the I/O interface. */
4512 pImage->VDIo.pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
4513 if (pImage->VDIo.pInterfaceIO)
4514 pImage->VDIo.pInterfaceIOCallbacks = VDGetInterfaceIO(pImage->VDIo.pInterfaceIO);
4515 else
4516 {
4517 rc = VDInterfaceAdd(&pImage->VDIo.VDIIO, "VD_IO", VDINTERFACETYPE_IO,
4518 &pDisk->VDIIOCallbacks, pDisk, &pVDIfsImage);
4519 pImage->VDIo.pInterfaceIO = &pImage->VDIo.VDIIO;
4520 pImage->VDIo.pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
4521 }
4522
4523 /* Set up the internal I/O interface. */
4524 AssertBreakStmt(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
4525 rc = VERR_INVALID_PARAMETER);
4526 rc = VDInterfaceAdd(&pImage->VDIo.VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
4527 &pDisk->VDIIOIntCallbacks, &pImage->VDIo, &pImage->pVDIfsImage);
4528 AssertRC(rc);
4529
4530 rc = vdFindBackend(pszBackend, &pImage->Backend);
4531 if (RT_FAILURE(rc))
4532 break;
4533 if (!pImage->Backend)
4534 {
4535 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4536 N_("VD: unknown backend name '%s'"), pszBackend);
4537 break;
4538 }
4539 if (!(pImage->Backend->uBackendCaps & ( VD_CAP_CREATE_FIXED
4540 | VD_CAP_CREATE_DYNAMIC)))
4541 {
4542 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4543 N_("VD: backend '%s' cannot create base images"), pszBackend);
4544 break;
4545 }
4546
4547 /* Create UUID if the caller didn't specify one. */
4548 if (!pUuid)
4549 {
4550 rc = RTUuidCreate(&uuid);
4551 if (RT_FAILURE(rc))
4552 {
4553 rc = vdError(pDisk, rc, RT_SRC_POS,
4554 N_("VD: cannot generate UUID for image '%s'"),
4555 pszFilename);
4556 break;
4557 }
4558 pUuid = &uuid;
4559 }
4560
4561 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
4562 uImageFlags &= ~VD_IMAGE_FLAGS_DIFF;
4563 rc = pImage->Backend->pfnCreate(pImage->pszFilename, cbSize,
4564 uImageFlags, pszComment, pPCHSGeometry,
4565 pLCHSGeometry, pUuid,
4566 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
4567 0, 99,
4568 pDisk->pVDIfsDisk,
4569 pImage->pVDIfsImage,
4570 pVDIfsOperation,
4571 &pImage->pBackendData);
4572
4573 if (RT_SUCCESS(rc))
4574 {
4575 pImage->VDIo.pBackendData = pImage->pBackendData;
4576 pImage->uImageFlags = uImageFlags;
4577
4578 /* Force sane optimization settings. It's not worth avoiding writes
4579 * to fixed size images. The overhead would have almost no payback. */
4580 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
4581 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
4582
4583 /* Lock disk for writing, as we modify pDisk information below. */
4584 rc2 = vdThreadStartWrite(pDisk);
4585 AssertRC(rc2);
4586 fLockWrite = true;
4587
4588 /** @todo optionally check UUIDs */
4589
4590 /* Re-check state, as the lock wasn't held and another image
4591 * creation call could have been done by another thread. */
4592 AssertMsgStmt(pDisk->cImages == 0,
4593 ("Create base image cannot be done with other images open\n"),
4594 rc = VERR_VD_INVALID_STATE);
4595 }
4596
4597 if (RT_SUCCESS(rc))
4598 {
4599 /* Cache disk information. */
4600 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
4601
4602 /* Cache PCHS geometry. */
4603 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
4604 &pDisk->PCHSGeometry);
4605 if (RT_FAILURE(rc2))
4606 {
4607 pDisk->PCHSGeometry.cCylinders = 0;
4608 pDisk->PCHSGeometry.cHeads = 0;
4609 pDisk->PCHSGeometry.cSectors = 0;
4610 }
4611 else
4612 {
4613 /* Make sure the CHS geometry is properly clipped. */
4614 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
4615 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
4616 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
4617 }
4618
4619 /* Cache LCHS geometry. */
4620 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
4621 &pDisk->LCHSGeometry);
4622 if (RT_FAILURE(rc2))
4623 {
4624 pDisk->LCHSGeometry.cCylinders = 0;
4625 pDisk->LCHSGeometry.cHeads = 0;
4626 pDisk->LCHSGeometry.cSectors = 0;
4627 }
4628 else
4629 {
4630 /* Make sure the CHS geometry is properly clipped. */
4631 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
4632 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
4633 }
4634
4635 /* Image successfully opened, make it the last image. */
4636 vdAddImageToList(pDisk, pImage);
4637 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
4638 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
4639 }
4640 else
4641 {
4642 /* Error detected, image may or may not be opened. Close and delete
4643 * image if it was opened. */
4644 if (pImage->pBackendData)
4645 {
4646 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, true);
4647 AssertRC(rc2);
4648 pImage->pBackendData = NULL;
4649 }
4650 }
4651 } while (0);
4652
4653 if (RT_UNLIKELY(fLockWrite))
4654 {
4655 rc2 = vdThreadFinishWrite(pDisk);
4656 AssertRC(rc2);
4657 }
4658 else if (RT_UNLIKELY(fLockRead))
4659 {
4660 rc2 = vdThreadFinishRead(pDisk);
4661 AssertRC(rc2);
4662 }
4663
4664 if (RT_FAILURE(rc))
4665 {
4666 if (pImage)
4667 {
4668 if (pImage->pszFilename)
4669 RTStrFree(pImage->pszFilename);
4670 RTMemFree(pImage);
4671 }
4672 }
4673
4674 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
4675 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
4676
4677 LogFlowFunc(("returns %Rrc\n", rc));
4678 return rc;
4679}
4680
4681/**
4682 * Creates and opens a new differencing image file in HDD container.
4683 * See comments for VDOpen function about differencing images.
4684 *
4685 * @returns VBox status code.
4686 * @param pDisk Pointer to HDD container.
4687 * @param pszBackend Name of the image file backend to use.
4688 * @param pszFilename Name of the differencing image file to create.
4689 * @param uImageFlags Flags specifying special image features.
4690 * @param pszComment Pointer to image comment. NULL is ok.
4691 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
4692 * @param pParentUuid New parent UUID of the image. If NULL, the UUID is queried automatically.
4693 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
4694 * @param pVDIfsImage Pointer to the per-image VD interface list.
4695 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
4696 */
4697VBOXDDU_DECL(int) VDCreateDiff(PVBOXHDD pDisk, const char *pszBackend,
4698 const char *pszFilename, unsigned uImageFlags,
4699 const char *pszComment, PCRTUUID pUuid,
4700 PCRTUUID pParentUuid, unsigned uOpenFlags,
4701 PVDINTERFACE pVDIfsImage,
4702 PVDINTERFACE pVDIfsOperation)
4703{
4704 int rc = VINF_SUCCESS;
4705 int rc2;
4706 bool fLockWrite = false, fLockRead = false;
4707 PVDIMAGE pImage = NULL;
4708 RTUUID uuid;
4709
4710 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
4711 pDisk, pszBackend, pszFilename, uImageFlags, pszComment, pUuid, uOpenFlags, pVDIfsImage, pVDIfsOperation));
4712
4713 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
4714 VDINTERFACETYPE_PROGRESS);
4715 PVDINTERFACEPROGRESS pCbProgress = NULL;
4716 if (pIfProgress)
4717 pCbProgress = VDGetInterfaceProgress(pIfProgress);
4718
4719 do
4720 {
4721 /* sanity check */
4722 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4723 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4724
4725 /* Check arguments. */
4726 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
4727 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
4728 rc = VERR_INVALID_PARAMETER);
4729 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
4730 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
4731 rc = VERR_INVALID_PARAMETER);
4732 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
4733 ("uImageFlags=%#x\n", uImageFlags),
4734 rc = VERR_INVALID_PARAMETER);
4735 /* The UUID may be NULL. */
4736 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
4737 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
4738 rc = VERR_INVALID_PARAMETER);
4739 /* The parent UUID may be NULL. */
4740 AssertMsgBreakStmt(pParentUuid == NULL || VALID_PTR(pParentUuid),
4741 ("pParentUuid=%#p ParentUUID=%RTuuid\n", pParentUuid, pParentUuid),
4742 rc = VERR_INVALID_PARAMETER);
4743 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
4744 ("uOpenFlags=%#x\n", uOpenFlags),
4745 rc = VERR_INVALID_PARAMETER);
4746
4747 /* Check state. Needs a temporary read lock. Holding the write lock
4748 * all the time would be blocking other activities for too long. */
4749 rc2 = vdThreadStartRead(pDisk);
4750 AssertRC(rc2);
4751 fLockRead = true;
4752 AssertMsgBreakStmt(pDisk->cImages != 0,
4753 ("Create diff image cannot be done without other images open\n"),
4754 rc = VERR_VD_INVALID_STATE);
4755 rc2 = vdThreadFinishRead(pDisk);
4756 AssertRC(rc2);
4757 fLockRead = false;
4758
4759 /* Set up image descriptor. */
4760 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
4761 if (!pImage)
4762 {
4763 rc = VERR_NO_MEMORY;
4764 break;
4765 }
4766 pImage->pszFilename = RTStrDup(pszFilename);
4767 if (!pImage->pszFilename)
4768 {
4769 rc = VERR_NO_MEMORY;
4770 break;
4771 }
4772
4773 rc = vdFindBackend(pszBackend, &pImage->Backend);
4774 if (RT_FAILURE(rc))
4775 break;
4776 if (!pImage->Backend)
4777 {
4778 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4779 N_("VD: unknown backend name '%s'"), pszBackend);
4780 break;
4781 }
4782 if ( !(pImage->Backend->uBackendCaps & VD_CAP_DIFF)
4783 || !(pImage->Backend->uBackendCaps & ( VD_CAP_CREATE_FIXED
4784 | VD_CAP_CREATE_DYNAMIC)))
4785 {
4786 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4787 N_("VD: backend '%s' cannot create diff images"), pszBackend);
4788 break;
4789 }
4790
4791 pImage->VDIo.pDisk = pDisk;
4792 pImage->pVDIfsImage = pVDIfsImage;
4793
4794 /* Set up the I/O interface. */
4795 pImage->VDIo.pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
4796 if (pImage->VDIo.pInterfaceIO)
4797 pImage->VDIo.pInterfaceIOCallbacks = VDGetInterfaceIO(pImage->VDIo.pInterfaceIO);
4798 else
4799 {
4800 rc = VDInterfaceAdd(&pImage->VDIo.VDIIO, "VD_IO", VDINTERFACETYPE_IO,
4801 &pDisk->VDIIOCallbacks, pDisk, &pVDIfsImage);
4802 pImage->VDIo.pInterfaceIO = &pImage->VDIo.VDIIO;
4803 pImage->VDIo.pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
4804 }
4805
4806 /* Set up the internal I/O interface. */
4807 AssertBreakStmt(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
4808 rc = VERR_INVALID_PARAMETER);
4809 rc = VDInterfaceAdd(&pImage->VDIo.VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
4810 &pDisk->VDIIOIntCallbacks, &pImage->VDIo, &pImage->pVDIfsImage);
4811 AssertRC(rc);
4812
4813 /* Create UUID if the caller didn't specify one. */
4814 if (!pUuid)
4815 {
4816 rc = RTUuidCreate(&uuid);
4817 if (RT_FAILURE(rc))
4818 {
4819 rc = vdError(pDisk, rc, RT_SRC_POS,
4820 N_("VD: cannot generate UUID for image '%s'"),
4821 pszFilename);
4822 break;
4823 }
4824 pUuid = &uuid;
4825 }
4826
4827 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
4828 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
4829 rc = pImage->Backend->pfnCreate(pImage->pszFilename, pDisk->cbSize,
4830 uImageFlags | VD_IMAGE_FLAGS_DIFF,
4831 pszComment, &pDisk->PCHSGeometry,
4832 &pDisk->LCHSGeometry, pUuid,
4833 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
4834 0, 99,
4835 pDisk->pVDIfsDisk,
4836 pImage->pVDIfsImage,
4837 pVDIfsOperation,
4838 &pImage->pBackendData);
4839
4840 if (RT_SUCCESS(rc))
4841 {
4842 pImage->VDIo.pBackendData = pImage->pBackendData;
4843 pImage->uImageFlags = uImageFlags;
4844
4845 /* Lock disk for writing, as we modify pDisk information below. */
4846 rc2 = vdThreadStartWrite(pDisk);
4847 AssertRC(rc2);
4848 fLockWrite = true;
4849
4850 /* Switch previous image to read-only mode. */
4851 unsigned uOpenFlagsPrevImg;
4852 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
4853 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
4854 {
4855 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
4856 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pBackendData, uOpenFlagsPrevImg);
4857 }
4858
4859 /** @todo optionally check UUIDs */
4860
4861 /* Re-check state, as the lock wasn't held and another image
4862 * creation call could have been done by another thread. */
4863 AssertMsgStmt(pDisk->cImages != 0,
4864 ("Create diff image cannot be done without other images open\n"),
4865 rc = VERR_VD_INVALID_STATE);
4866 }
4867
4868 if (RT_SUCCESS(rc))
4869 {
4870 RTUUID Uuid;
4871 RTTIMESPEC ts;
4872
4873 if (pParentUuid && !RTUuidIsNull(pParentUuid))
4874 {
4875 Uuid = *pParentUuid;
4876 pImage->Backend->pfnSetParentUuid(pImage->pBackendData, &Uuid);
4877 }
4878 else
4879 {
4880 rc2 = pDisk->pLast->Backend->pfnGetUuid(pDisk->pLast->pBackendData,
4881 &Uuid);
4882 if (RT_SUCCESS(rc2))
4883 pImage->Backend->pfnSetParentUuid(pImage->pBackendData, &Uuid);
4884 }
4885 rc2 = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
4886 &Uuid);
4887 if (RT_SUCCESS(rc2))
4888 pImage->Backend->pfnSetParentModificationUuid(pImage->pBackendData,
4889 &Uuid);
4890 if (pDisk->pLast->Backend->pfnGetTimeStamp)
4891 rc2 = pDisk->pLast->Backend->pfnGetTimeStamp(pDisk->pLast->pBackendData,
4892 &ts);
4893 else
4894 rc2 = VERR_NOT_IMPLEMENTED;
4895 if (RT_SUCCESS(rc2) && pImage->Backend->pfnSetParentTimeStamp)
4896 pImage->Backend->pfnSetParentTimeStamp(pImage->pBackendData, &ts);
4897
4898 if (pImage->Backend->pfnSetParentFilename)
4899 rc2 = pImage->Backend->pfnSetParentFilename(pImage->pBackendData, pDisk->pLast->pszFilename);
4900 }
4901
4902 if (RT_SUCCESS(rc))
4903 {
4904 /* Image successfully opened, make it the last image. */
4905 vdAddImageToList(pDisk, pImage);
4906 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
4907 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
4908 }
4909 else
4910 {
4911 /* Error detected, but image opened. Close and delete image. */
4912 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, true);
4913 AssertRC(rc2);
4914 pImage->pBackendData = NULL;
4915 }
4916 } while (0);
4917
4918 if (RT_UNLIKELY(fLockWrite))
4919 {
4920 rc2 = vdThreadFinishWrite(pDisk);
4921 AssertRC(rc2);
4922 }
4923 else if (RT_UNLIKELY(fLockRead))
4924 {
4925 rc2 = vdThreadFinishRead(pDisk);
4926 AssertRC(rc2);
4927 }
4928
4929 if (RT_FAILURE(rc))
4930 {
4931 if (pImage)
4932 {
4933 if (pImage->pszFilename)
4934 RTStrFree(pImage->pszFilename);
4935 RTMemFree(pImage);
4936 }
4937 }
4938
4939 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
4940 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
4941
4942 LogFlowFunc(("returns %Rrc\n", rc));
4943 return rc;
4944}
4945
4946
4947/**
4948 * Creates and opens new cache image file in HDD container.
4949 *
4950 * @return VBox status code.
4951 * @param pDisk Name of the cache file backend to use (case insensitive).
4952 * @param pszFilename Name of the differencing cache file to create.
4953 * @param cbSize Maximum size of the cache.
4954 * @param uImageFlags Flags specifying special cache features.
4955 * @param pszComment Pointer to image comment. NULL is ok.
4956 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
4957 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
4958 * @param pVDIfsCache Pointer to the per-cache VD interface list.
4959 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
4960 */
4961VBOXDDU_DECL(int) VDCreateCache(PVBOXHDD pDisk, const char *pszBackend,
4962 const char *pszFilename, uint64_t cbSize,
4963 unsigned uImageFlags, const char *pszComment,
4964 PCRTUUID pUuid, unsigned uOpenFlags,
4965 PVDINTERFACE pVDIfsCache, PVDINTERFACE pVDIfsOperation)
4966{
4967 int rc = VINF_SUCCESS;
4968 int rc2;
4969 bool fLockWrite = false, fLockRead = false;
4970 PVDCACHE pCache = NULL;
4971 RTUUID uuid;
4972
4973 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
4974 pDisk, pszBackend, pszFilename, cbSize, uImageFlags, pszComment, pUuid, uOpenFlags, pVDIfsCache, pVDIfsOperation));
4975
4976 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
4977 VDINTERFACETYPE_PROGRESS);
4978 PVDINTERFACEPROGRESS pCbProgress = NULL;
4979 if (pIfProgress)
4980 pCbProgress = VDGetInterfaceProgress(pIfProgress);
4981
4982 do
4983 {
4984 /* sanity check */
4985 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4986 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4987
4988 /* Check arguments. */
4989 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
4990 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
4991 rc = VERR_INVALID_PARAMETER);
4992 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
4993 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
4994 rc = VERR_INVALID_PARAMETER);
4995 AssertMsgBreakStmt(cbSize,
4996 ("cbSize=%llu\n", cbSize),
4997 rc = VERR_INVALID_PARAMETER);
4998 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
4999 ("uImageFlags=%#x\n", uImageFlags),
5000 rc = VERR_INVALID_PARAMETER);
5001 /* The UUID may be NULL. */
5002 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
5003 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
5004 rc = VERR_INVALID_PARAMETER);
5005 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
5006 ("uOpenFlags=%#x\n", uOpenFlags),
5007 rc = VERR_INVALID_PARAMETER);
5008
5009 /* Check state. Needs a temporary read lock. Holding the write lock
5010 * all the time would be blocking other activities for too long. */
5011 rc2 = vdThreadStartRead(pDisk);
5012 AssertRC(rc2);
5013 fLockRead = true;
5014 AssertMsgBreakStmt(!pDisk->pCache,
5015 ("Create cache image cannot be done with a cache already attached\n"),
5016 rc = VERR_VD_CACHE_ALREADY_EXISTS);
5017 rc2 = vdThreadFinishRead(pDisk);
5018 AssertRC(rc2);
5019 fLockRead = false;
5020
5021 /* Set up image descriptor. */
5022 pCache = (PVDCACHE)RTMemAllocZ(sizeof(VDCACHE));
5023 if (!pCache)
5024 {
5025 rc = VERR_NO_MEMORY;
5026 break;
5027 }
5028 pCache->pszFilename = RTStrDup(pszFilename);
5029 if (!pCache->pszFilename)
5030 {
5031 rc = VERR_NO_MEMORY;
5032 break;
5033 }
5034
5035 rc = vdFindCacheBackend(pszBackend, &pCache->Backend);
5036 if (RT_FAILURE(rc))
5037 break;
5038 if (!pCache->Backend)
5039 {
5040 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
5041 N_("VD: unknown backend name '%s'"), pszBackend);
5042 break;
5043 }
5044
5045 pCache->VDIo.pDisk = pDisk;
5046 pCache->pVDIfsCache = pVDIfsCache;
5047
5048 /* Set up the I/O interface. */
5049 pCache->VDIo.pInterfaceIO = VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IO);
5050 if (pCache->VDIo.pInterfaceIO)
5051 pCache->VDIo.pInterfaceIOCallbacks = VDGetInterfaceIO(pCache->VDIo.pInterfaceIO);
5052 else
5053 {
5054 rc = VDInterfaceAdd(&pCache->VDIo.VDIIO, "VD_IO", VDINTERFACETYPE_IO,
5055 &pDisk->VDIIOCallbacks, pDisk, &pVDIfsCache);
5056 pCache->VDIo.pInterfaceIO = &pCache->VDIo.VDIIO;
5057 pCache->VDIo.pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
5058 }
5059
5060 /* Set up the internal I/O interface. */
5061 AssertBreakStmt(!VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IOINT),
5062 rc = VERR_INVALID_PARAMETER);
5063 rc = VDInterfaceAdd(&pCache->VDIo.VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
5064 &pDisk->VDIIOIntCallbacks, &pCache->VDIo, &pCache->pVDIfsCache);
5065 AssertRC(rc);
5066
5067 /* Create UUID if the caller didn't specify one. */
5068 if (!pUuid)
5069 {
5070 rc = RTUuidCreate(&uuid);
5071 if (RT_FAILURE(rc))
5072 {
5073 rc = vdError(pDisk, rc, RT_SRC_POS,
5074 N_("VD: cannot generate UUID for image '%s'"),
5075 pszFilename);
5076 break;
5077 }
5078 pUuid = &uuid;
5079 }
5080
5081 pCache->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
5082 rc = pCache->Backend->pfnCreate(pCache->pszFilename, cbSize,
5083 uImageFlags,
5084 pszComment, pUuid,
5085 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
5086 0, 99,
5087 pDisk->pVDIfsDisk,
5088 pCache->pVDIfsCache,
5089 pVDIfsOperation,
5090 &pCache->pBackendData);
5091
5092 if (RT_SUCCESS(rc))
5093 {
5094 /* Lock disk for writing, as we modify pDisk information below. */
5095 rc2 = vdThreadStartWrite(pDisk);
5096 AssertRC(rc2);
5097 fLockWrite = true;
5098
5099 pCache->VDIo.pBackendData = pCache->pBackendData;
5100
5101 /* Re-check state, as the lock wasn't held and another image
5102 * creation call could have been done by another thread. */
5103 AssertMsgStmt(!pDisk->pCache,
5104 ("Create cache image cannot be done with another cache open\n"),
5105 rc = VERR_VD_CACHE_ALREADY_EXISTS);
5106 }
5107
5108 if ( RT_SUCCESS(rc)
5109 && pDisk->pLast)
5110 {
5111 RTUUID UuidModification;
5112
5113 /* Set same modification Uuid as the last image. */
5114 rc = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
5115 &UuidModification);
5116 if (RT_SUCCESS(rc))
5117 {
5118 rc = pCache->Backend->pfnSetModificationUuid(pCache->pBackendData,
5119 &UuidModification);
5120 }
5121
5122 if (rc == VERR_NOT_SUPPORTED)
5123 rc = VINF_SUCCESS;
5124 }
5125
5126 if (RT_SUCCESS(rc))
5127 {
5128 /* Cache successfully created. */
5129 pDisk->pCache = pCache;
5130 }
5131 else
5132 {
5133 /* Error detected, but image opened. Close and delete image. */
5134 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, true);
5135 AssertRC(rc2);
5136 pCache->pBackendData = NULL;
5137 }
5138 } while (0);
5139
5140 if (RT_UNLIKELY(fLockWrite))
5141 {
5142 rc2 = vdThreadFinishWrite(pDisk);
5143 AssertRC(rc2);
5144 }
5145 else if (RT_UNLIKELY(fLockRead))
5146 {
5147 rc2 = vdThreadFinishRead(pDisk);
5148 AssertRC(rc2);
5149 }
5150
5151 if (RT_FAILURE(rc))
5152 {
5153 if (pCache)
5154 {
5155 if (pCache->pszFilename)
5156 RTStrFree(pCache->pszFilename);
5157 RTMemFree(pCache);
5158 }
5159 }
5160
5161 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
5162 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
5163
5164 LogFlowFunc(("returns %Rrc\n", rc));
5165 return rc;
5166}
5167
5168/**
5169 * Merges two images (not necessarily with direct parent/child relationship).
5170 * As a side effect the source image and potentially the other images which
5171 * are also merged to the destination are deleted from both the disk and the
5172 * images in the HDD container.
5173 *
5174 * @returns VBox status code.
5175 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
5176 * @param pDisk Pointer to HDD container.
5177 * @param nImageFrom Name of the image file to merge from.
5178 * @param nImageTo Name of the image file to merge to.
5179 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
5180 */
5181VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
5182 unsigned nImageTo, PVDINTERFACE pVDIfsOperation)
5183{
5184 int rc = VINF_SUCCESS;
5185 int rc2;
5186 bool fLockWrite = false, fLockRead = false;
5187 void *pvBuf = NULL;
5188
5189 LogFlowFunc(("pDisk=%#p nImageFrom=%u nImageTo=%u pVDIfsOperation=%#p\n",
5190 pDisk, nImageFrom, nImageTo, pVDIfsOperation));
5191
5192 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
5193 VDINTERFACETYPE_PROGRESS);
5194 PVDINTERFACEPROGRESS pCbProgress = NULL;
5195 if (pIfProgress)
5196 pCbProgress = VDGetInterfaceProgress(pIfProgress);
5197
5198 do
5199 {
5200 /* sanity check */
5201 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
5202 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5203
5204 /* For simplicity reasons lock for writing as the image reopen below
5205 * might need it. After all the reopen is usually needed. */
5206 rc2 = vdThreadStartWrite(pDisk);
5207 AssertRC(rc2);
5208 fLockWrite = true;
5209 PVDIMAGE pImageFrom = vdGetImageByNumber(pDisk, nImageFrom);
5210 PVDIMAGE pImageTo = vdGetImageByNumber(pDisk, nImageTo);
5211 if (!pImageFrom || !pImageTo)
5212 {
5213 rc = VERR_VD_IMAGE_NOT_FOUND;
5214 break;
5215 }
5216 AssertBreakStmt(pImageFrom != pImageTo, rc = VERR_INVALID_PARAMETER);
5217
5218 /* Make sure destination image is writable. */
5219 unsigned uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pBackendData);
5220 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
5221 {
5222 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
5223 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
5224 uOpenFlags);
5225 if (RT_FAILURE(rc))
5226 break;
5227 }
5228
5229 /* Get size of destination image. */
5230 uint64_t cbSize = pImageTo->Backend->pfnGetSize(pImageTo->pBackendData);
5231 rc2 = vdThreadFinishWrite(pDisk);
5232 AssertRC(rc2);
5233 fLockWrite = false;
5234
5235 /* Allocate tmp buffer. */
5236 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
5237 if (!pvBuf)
5238 {
5239 rc = VERR_NO_MEMORY;
5240 break;
5241 }
5242
5243 /* Merging is done directly on the images itself. This potentially
5244 * causes trouble if the disk is full in the middle of operation. */
5245 if (nImageFrom < nImageTo)
5246 {
5247 /* Merge parent state into child. This means writing all not
5248 * allocated blocks in the destination image which are allocated in
5249 * the images to be merged. */
5250 uint64_t uOffset = 0;
5251 uint64_t cbRemaining = cbSize;
5252 do
5253 {
5254 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
5255
5256 /* Need to hold the write lock during a read-write operation. */
5257 rc2 = vdThreadStartWrite(pDisk);
5258 AssertRC(rc2);
5259 fLockWrite = true;
5260
5261 rc = pImageTo->Backend->pfnRead(pImageTo->pBackendData,
5262 uOffset, pvBuf, cbThisRead,
5263 &cbThisRead);
5264 if (rc == VERR_VD_BLOCK_FREE)
5265 {
5266 /* Search for image with allocated block. Do not attempt to
5267 * read more than the previous reads marked as valid.
5268 * Otherwise this would return stale data when different
5269 * block sizes are used for the images. */
5270 for (PVDIMAGE pCurrImage = pImageTo->pPrev;
5271 pCurrImage != NULL && pCurrImage != pImageFrom->pPrev && rc == VERR_VD_BLOCK_FREE;
5272 pCurrImage = pCurrImage->pPrev)
5273 {
5274 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
5275 uOffset, pvBuf,
5276 cbThisRead,
5277 &cbThisRead);
5278 }
5279
5280 if (rc != VERR_VD_BLOCK_FREE)
5281 {
5282 if (RT_FAILURE(rc))
5283 break;
5284 /* Updating the cache is required because this might be a live merge. */
5285 rc = vdWriteHelper(pDisk, pImageTo, pImageFrom->pPrev,
5286 uOffset, pvBuf, cbThisRead,
5287 true /* fUpdateCache */);
5288 if (RT_FAILURE(rc))
5289 break;
5290 }
5291 else
5292 rc = VINF_SUCCESS;
5293 }
5294 else if (RT_FAILURE(rc))
5295 break;
5296
5297 rc2 = vdThreadFinishWrite(pDisk);
5298 AssertRC(rc2);
5299 fLockWrite = false;
5300
5301 uOffset += cbThisRead;
5302 cbRemaining -= cbThisRead;
5303
5304 if (pCbProgress && pCbProgress->pfnProgress)
5305 {
5306 /** @todo r=klaus: this can update the progress to the same
5307 * percentage over and over again if the image format makes
5308 * relatively small increments. */
5309 rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
5310 uOffset * 99 / cbSize);
5311 if (RT_FAILURE(rc))
5312 break;
5313 }
5314 } while (uOffset < cbSize);
5315 }
5316 else
5317 {
5318 /*
5319 * We may need to update the parent uuid of the child coming after
5320 * the last image to be merged. We have to reopen it read/write.
5321 *
5322 * This is done before we do the actual merge to prevent an
5323 * inconsistent chain if the mode change fails for some reason.
5324 */
5325 if (pImageFrom->pNext)
5326 {
5327 PVDIMAGE pImageChild = pImageFrom->pNext;
5328
5329 /* Take the write lock. */
5330 rc2 = vdThreadStartWrite(pDisk);
5331 AssertRC(rc2);
5332 fLockWrite = true;
5333
5334 /* We need to open the image in read/write mode. */
5335 uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pBackendData);
5336
5337 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
5338 {
5339 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
5340 rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pBackendData,
5341 uOpenFlags);
5342 if (RT_FAILURE(rc))
5343 break;
5344 }
5345
5346 rc2 = vdThreadFinishWrite(pDisk);
5347 AssertRC(rc2);
5348 fLockWrite = false;
5349 }
5350
5351 /* If the merge is from the last image we have to relay all writes
5352 * to the merge destination as well, so that concurrent writes
5353 * (in case of a live merge) are handled correctly. */
5354 if (!pImageFrom->pNext)
5355 {
5356 /* Take the write lock. */
5357 rc2 = vdThreadStartWrite(pDisk);
5358 AssertRC(rc2);
5359 fLockWrite = true;
5360
5361 pDisk->pImageRelay = pImageTo;
5362
5363 rc2 = vdThreadFinishWrite(pDisk);
5364 AssertRC(rc2);
5365 fLockWrite = false;
5366 }
5367
5368 /* Merge child state into parent. This means writing all blocks
5369 * which are allocated in the image up to the source image to the
5370 * destination image. */
5371 uint64_t uOffset = 0;
5372 uint64_t cbRemaining = cbSize;
5373 do
5374 {
5375 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
5376 rc = VERR_VD_BLOCK_FREE;
5377
5378 /* Need to hold the write lock during a read-write operation. */
5379 rc2 = vdThreadStartWrite(pDisk);
5380 AssertRC(rc2);
5381 fLockWrite = true;
5382
5383 /* Search for image with allocated block. Do not attempt to
5384 * read more than the previous reads marked as valid. Otherwise
5385 * this would return stale data when different block sizes are
5386 * used for the images. */
5387 for (PVDIMAGE pCurrImage = pImageFrom;
5388 pCurrImage != NULL && pCurrImage != pImageTo && rc == VERR_VD_BLOCK_FREE;
5389 pCurrImage = pCurrImage->pPrev)
5390 {
5391 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
5392 uOffset, pvBuf,
5393 cbThisRead, &cbThisRead);
5394 }
5395
5396 if (rc != VERR_VD_BLOCK_FREE)
5397 {
5398 if (RT_FAILURE(rc))
5399 break;
5400 rc = vdWriteHelper(pDisk, pImageTo, NULL, uOffset, pvBuf,
5401 cbThisRead, true /* fUpdateCache */);
5402 if (RT_FAILURE(rc))
5403 break;
5404 }
5405 else
5406 rc = VINF_SUCCESS;
5407
5408 rc2 = vdThreadFinishWrite(pDisk);
5409 AssertRC(rc2);
5410 fLockWrite = false;
5411
5412 uOffset += cbThisRead;
5413 cbRemaining -= cbThisRead;
5414
5415 if (pCbProgress && pCbProgress->pfnProgress)
5416 {
5417 /** @todo r=klaus: this can update the progress to the same
5418 * percentage over and over again if the image format makes
5419 * relatively small increments. */
5420 rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
5421 uOffset * 99 / cbSize);
5422 if (RT_FAILURE(rc))
5423 break;
5424 }
5425 } while (uOffset < cbSize);
5426
5427 /* In case we set up a "write proxy" image above we must clear
5428 * this again now to prevent stray writes. Failure or not. */
5429 if (!pImageFrom->pNext)
5430 {
5431 /* Take the write lock. */
5432 rc2 = vdThreadStartWrite(pDisk);
5433 AssertRC(rc2);
5434 fLockWrite = true;
5435
5436 pDisk->pImageRelay = NULL;
5437
5438 rc2 = vdThreadFinishWrite(pDisk);
5439 AssertRC(rc2);
5440 fLockWrite = false;
5441 }
5442 }
5443
5444 /*
5445 * Leave in case of an error to avoid corrupted data in the image chain
5446 * (includes cancelling the operation by the user).
5447 */
5448 if (RT_FAILURE(rc))
5449 break;
5450
5451 /* Need to hold the write lock while finishing the merge. */
5452 rc2 = vdThreadStartWrite(pDisk);
5453 AssertRC(rc2);
5454 fLockWrite = true;
5455
5456 /* Update parent UUID so that image chain is consistent. */
5457 RTUUID Uuid;
5458 PVDIMAGE pImageChild = NULL;
5459 if (nImageFrom < nImageTo)
5460 {
5461 if (pImageFrom->pPrev)
5462 {
5463 rc = pImageFrom->pPrev->Backend->pfnGetUuid(pImageFrom->pPrev->pBackendData,
5464 &Uuid);
5465 AssertRC(rc);
5466 }
5467 else
5468 RTUuidClear(&Uuid);
5469 rc = pImageTo->Backend->pfnSetParentUuid(pImageTo->pBackendData,
5470 &Uuid);
5471 AssertRC(rc);
5472 }
5473 else
5474 {
5475 /* Update the parent uuid of the child of the last merged image. */
5476 if (pImageFrom->pNext)
5477 {
5478 rc = pImageTo->Backend->pfnGetUuid(pImageTo->pBackendData,
5479 &Uuid);
5480 AssertRC(rc);
5481
5482 rc = pImageFrom->Backend->pfnSetParentUuid(pImageFrom->pNext->pBackendData,
5483 &Uuid);
5484 AssertRC(rc);
5485
5486 pImageChild = pImageFrom->pNext;
5487 }
5488 }
5489
5490 /* Delete the no longer needed images. */
5491 PVDIMAGE pImg = pImageFrom, pTmp;
5492 while (pImg != pImageTo)
5493 {
5494 if (nImageFrom < nImageTo)
5495 pTmp = pImg->pNext;
5496 else
5497 pTmp = pImg->pPrev;
5498 vdRemoveImageFromList(pDisk, pImg);
5499 pImg->Backend->pfnClose(pImg->pBackendData, true);
5500 RTMemFree(pImg->pszFilename);
5501 RTMemFree(pImg);
5502 pImg = pTmp;
5503 }
5504
5505 /* Make sure destination image is back to read only if necessary. */
5506 if (pImageTo != pDisk->pLast)
5507 {
5508 uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pBackendData);
5509 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
5510 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
5511 uOpenFlags);
5512 if (RT_FAILURE(rc))
5513 break;
5514 }
5515
5516 /*
5517 * Make sure the child is readonly
5518 * for the child -> parent merge direction
5519 * if necessary.
5520 */
5521 if ( nImageFrom > nImageTo
5522 && pImageChild
5523 && pImageChild != pDisk->pLast)
5524 {
5525 uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pBackendData);
5526 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
5527 rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pBackendData,
5528 uOpenFlags);
5529 if (RT_FAILURE(rc))
5530 break;
5531 }
5532 } while (0);
5533
5534 if (RT_UNLIKELY(fLockWrite))
5535 {
5536 rc2 = vdThreadFinishWrite(pDisk);
5537 AssertRC(rc2);
5538 }
5539 else if (RT_UNLIKELY(fLockRead))
5540 {
5541 rc2 = vdThreadFinishRead(pDisk);
5542 AssertRC(rc2);
5543 }
5544
5545 if (pvBuf)
5546 RTMemTmpFree(pvBuf);
5547
5548 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
5549 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
5550
5551 LogFlowFunc(("returns %Rrc\n", rc));
5552 return rc;
5553}
5554
5555/**
5556 * Copies an image from one HDD container to another.
5557 * The copy is opened in the target HDD container.
5558 * It is possible to convert between different image formats, because the
5559 * backend for the destination may be different from the source.
5560 * If both the source and destination reference the same HDD container,
5561 * then the image is moved (by copying/deleting or renaming) to the new location.
5562 * The source container is unchanged if the move operation fails, otherwise
5563 * the image at the new location is opened in the same way as the old one was.
5564 *
5565 * @returns VBox status code.
5566 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
5567 * @param pDiskFrom Pointer to source HDD container.
5568 * @param nImage Image number, counts from 0. 0 is always base image of container.
5569 * @param pDiskTo Pointer to destination HDD container.
5570 * @param pszBackend Name of the image file backend to use.
5571 * @param pszFilename New name of the image (may be NULL if pDiskFrom == pDiskTo).
5572 * @param fMoveByRename If true, attempt to perform a move by renaming (if successful the new size is ignored).
5573 * @param cbSize New image size (0 means leave unchanged).
5574 * @param uImageFlags Flags specifying special destination image features.
5575 * @param pDstUuid New UUID of the destination image. If NULL, a new UUID is created.
5576 * This parameter is used if and only if a true copy is created.
5577 * In all rename/move cases the UUIDs are copied over.
5578 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
5579 * Only used if the destination image is created.
5580 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
5581 * @param pDstVDIfsImage Pointer to the per-image VD interface list, for the
5582 * destination image.
5583 * @param pDstVDIfsOperation Pointer to the per-image VD interface list,
5584 * for the destination image.
5585 */
5586VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
5587 const char *pszBackend, const char *pszFilename,
5588 bool fMoveByRename, uint64_t cbSize,
5589 unsigned uImageFlags, PCRTUUID pDstUuid,
5590 unsigned uOpenFlags, PVDINTERFACE pVDIfsOperation,
5591 PVDINTERFACE pDstVDIfsImage,
5592 PVDINTERFACE pDstVDIfsOperation)
5593{
5594 int rc = VINF_SUCCESS;
5595 int rc2;
5596 bool fLockReadFrom = false, fLockWriteFrom = false, fLockWriteTo = false;
5597 void *pvBuf = NULL;
5598 PVDIMAGE pImageTo = NULL;
5599
5600 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",
5601 pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename, cbSize, uImageFlags, pDstUuid, uOpenFlags, pVDIfsOperation, pDstVDIfsImage, pDstVDIfsOperation));
5602
5603 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
5604 VDINTERFACETYPE_PROGRESS);
5605 PVDINTERFACEPROGRESS pCbProgress = NULL;
5606 if (pIfProgress)
5607 pCbProgress = VDGetInterfaceProgress(pIfProgress);
5608
5609 PVDINTERFACE pDstIfProgress = VDInterfaceGet(pDstVDIfsOperation,
5610 VDINTERFACETYPE_PROGRESS);
5611 PVDINTERFACEPROGRESS pDstCbProgress = NULL;
5612 if (pDstIfProgress)
5613 pDstCbProgress = VDGetInterfaceProgress(pDstIfProgress);
5614
5615 do {
5616 /* Check arguments. */
5617 AssertMsgBreakStmt(VALID_PTR(pDiskFrom), ("pDiskFrom=%#p\n", pDiskFrom),
5618 rc = VERR_INVALID_PARAMETER);
5619 AssertMsg(pDiskFrom->u32Signature == VBOXHDDDISK_SIGNATURE,
5620 ("u32Signature=%08x\n", pDiskFrom->u32Signature));
5621
5622 rc2 = vdThreadStartRead(pDiskFrom);
5623 AssertRC(rc2);
5624 fLockReadFrom = true;
5625 PVDIMAGE pImageFrom = vdGetImageByNumber(pDiskFrom, nImage);
5626 AssertPtrBreakStmt(pImageFrom, rc = VERR_VD_IMAGE_NOT_FOUND);
5627 AssertMsgBreakStmt(VALID_PTR(pDiskTo), ("pDiskTo=%#p\n", pDiskTo),
5628 rc = VERR_INVALID_PARAMETER);
5629 AssertMsg(pDiskTo->u32Signature == VBOXHDDDISK_SIGNATURE,
5630 ("u32Signature=%08x\n", pDiskTo->u32Signature));
5631
5632 /* Move the image. */
5633 if (pDiskFrom == pDiskTo)
5634 {
5635 /* Rename only works when backends are the same, are file based
5636 * and the rename method is implemented. */
5637 if ( fMoveByRename
5638 && !RTStrICmp(pszBackend, pImageFrom->Backend->pszBackendName)
5639 && pImageFrom->Backend->uBackendCaps & VD_CAP_FILE
5640 && pImageFrom->Backend->pfnRename)
5641 {
5642 rc2 = vdThreadFinishRead(pDiskFrom);
5643 AssertRC(rc2);
5644 fLockReadFrom = false;
5645
5646 rc2 = vdThreadStartWrite(pDiskFrom);
5647 AssertRC(rc2);
5648 fLockWriteFrom = true;
5649 rc = pImageFrom->Backend->pfnRename(pImageFrom->pBackendData, pszFilename ? pszFilename : pImageFrom->pszFilename);
5650 break;
5651 }
5652
5653 /** @todo Moving (including shrinking/growing) of the image is
5654 * requested, but the rename attempt failed or it wasn't possible.
5655 * Must now copy image to temp location. */
5656 AssertReleaseMsgFailed(("VDCopy: moving by copy/delete not implemented\n"));
5657 }
5658
5659 /* pszFilename is allowed to be NULL, as this indicates copy to the existing image. */
5660 AssertMsgBreakStmt(pszFilename == NULL || (VALID_PTR(pszFilename) && *pszFilename),
5661 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
5662 rc = VERR_INVALID_PARAMETER);
5663
5664 uint64_t cbSizeFrom;
5665 cbSizeFrom = pImageFrom->Backend->pfnGetSize(pImageFrom->pBackendData);
5666 if (cbSizeFrom == 0)
5667 {
5668 rc = VERR_VD_VALUE_NOT_FOUND;
5669 break;
5670 }
5671
5672 VDGEOMETRY PCHSGeometryFrom = {0, 0, 0};
5673 VDGEOMETRY LCHSGeometryFrom = {0, 0, 0};
5674 pImageFrom->Backend->pfnGetPCHSGeometry(pImageFrom->pBackendData, &PCHSGeometryFrom);
5675 pImageFrom->Backend->pfnGetLCHSGeometry(pImageFrom->pBackendData, &LCHSGeometryFrom);
5676
5677 RTUUID ImageUuid, ImageModificationUuid;
5678 if (pDiskFrom != pDiskTo)
5679 {
5680 if (pDstUuid)
5681 ImageUuid = *pDstUuid;
5682 else
5683 RTUuidCreate(&ImageUuid);
5684 }
5685 else
5686 {
5687 rc = pImageFrom->Backend->pfnGetUuid(pImageFrom->pBackendData, &ImageUuid);
5688 if (RT_FAILURE(rc))
5689 RTUuidCreate(&ImageUuid);
5690 }
5691 rc = pImageFrom->Backend->pfnGetModificationUuid(pImageFrom->pBackendData, &ImageModificationUuid);
5692 if (RT_FAILURE(rc))
5693 RTUuidClear(&ImageModificationUuid);
5694
5695 char szComment[1024];
5696 rc = pImageFrom->Backend->pfnGetComment(pImageFrom->pBackendData, szComment, sizeof(szComment));
5697 if (RT_FAILURE(rc))
5698 szComment[0] = '\0';
5699 else
5700 szComment[sizeof(szComment) - 1] = '\0';
5701
5702 rc2 = vdThreadFinishRead(pDiskFrom);
5703 AssertRC(rc2);
5704 fLockReadFrom = false;
5705
5706 rc2 = vdThreadStartRead(pDiskTo);
5707 AssertRC(rc2);
5708 unsigned cImagesTo = pDiskTo->cImages;
5709 rc2 = vdThreadFinishRead(pDiskTo);
5710 AssertRC(rc2);
5711
5712 if (pszFilename)
5713 {
5714 if (cbSize == 0)
5715 cbSize = cbSizeFrom;
5716
5717 /* Create destination image with the properties of source image. */
5718 /** @todo replace the VDCreateDiff/VDCreateBase calls by direct
5719 * calls to the backend. Unifies the code and reduces the API
5720 * dependencies. Would also make the synchronization explicit. */
5721 if (cImagesTo > 0)
5722 {
5723 rc = VDCreateDiff(pDiskTo, pszBackend, pszFilename,
5724 uImageFlags, szComment, &ImageUuid,
5725 NULL /* pParentUuid */,
5726 uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
5727 pDstVDIfsImage, NULL);
5728
5729 rc2 = vdThreadStartWrite(pDiskTo);
5730 AssertRC(rc2);
5731 fLockWriteTo = true;
5732 } else {
5733 /** @todo hack to force creation of a fixed image for
5734 * the RAW backend, which can't handle anything else. */
5735 if (!RTStrICmp(pszBackend, "RAW"))
5736 uImageFlags |= VD_IMAGE_FLAGS_FIXED;
5737
5738 /* Fix broken PCHS geometry. Can happen for two reasons: either
5739 * the backend mixes up PCHS and LCHS, or the application used
5740 * to create the source image has put garbage in it. */
5741 /** @todo double-check if the VHD backend correctly handles
5742 * PCHS and LCHS geometry. also reconsider our current paranoia
5743 * level when it comes to geometry settings here and in the
5744 * backends. */
5745 if (PCHSGeometryFrom.cHeads > 16 || PCHSGeometryFrom.cSectors > 63)
5746 {
5747 Assert(RT_MIN(cbSize / 512 / 16 / 63, 16383) - (uint32_t)RT_MIN(cbSize / 512 / 16 / 63, 16383));
5748 PCHSGeometryFrom.cCylinders = (uint32_t)RT_MIN(cbSize / 512 / 16 / 63, 16383);
5749 PCHSGeometryFrom.cHeads = 16;
5750 PCHSGeometryFrom.cSectors = 63;
5751 }
5752
5753 rc = VDCreateBase(pDiskTo, pszBackend, pszFilename, cbSize,
5754 uImageFlags, szComment,
5755 &PCHSGeometryFrom, &LCHSGeometryFrom,
5756 NULL, uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
5757 pDstVDIfsImage, NULL);
5758
5759 rc2 = vdThreadStartWrite(pDiskTo);
5760 AssertRC(rc2);
5761 fLockWriteTo = true;
5762
5763 if (RT_SUCCESS(rc) && !RTUuidIsNull(&ImageUuid))
5764 pDiskTo->pLast->Backend->pfnSetUuid(pDiskTo->pLast->pBackendData, &ImageUuid);
5765 }
5766 if (RT_FAILURE(rc))
5767 break;
5768
5769 pImageTo = pDiskTo->pLast;
5770 AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
5771
5772 cbSize = RT_MIN(cbSize, cbSizeFrom);
5773 }
5774 else
5775 {
5776 pImageTo = pDiskTo->pLast;
5777 AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
5778
5779 uint64_t cbSizeTo;
5780 cbSizeTo = pImageTo->Backend->pfnGetSize(pImageTo->pBackendData);
5781 if (cbSizeTo == 0)
5782 {
5783 rc = VERR_VD_VALUE_NOT_FOUND;
5784 break;
5785 }
5786
5787 if (cbSize == 0)
5788 cbSize = RT_MIN(cbSizeFrom, cbSizeTo);
5789 }
5790
5791 rc2 = vdThreadFinishWrite(pDiskTo);
5792 AssertRC(rc2);
5793 fLockWriteTo = false;
5794
5795 /* Allocate tmp buffer. */
5796 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
5797 if (!pvBuf)
5798 {
5799 rc = VERR_NO_MEMORY;
5800 break;
5801 }
5802
5803 /* Whether we can take the optimized copy path (false) or not.
5804 * Don't optimize if the image existed or if it is a child image. */
5805 bool fRegularRead = (pszFilename == NULL) || (cImagesTo > 0);
5806
5807 /* Copy the data. */
5808 uint64_t uOffset = 0;
5809 uint64_t cbRemaining = cbSize;
5810
5811 do
5812 {
5813 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
5814
5815 /* Note that we don't attempt to synchronize cross-disk accesses.
5816 * It wouldn't be very difficult to do, just the lock order would
5817 * need to be defined somehow to prevent deadlocks. Postpone such
5818 * magic as there is no use case for this. */
5819
5820 rc2 = vdThreadStartRead(pDiskFrom);
5821 AssertRC(rc2);
5822 fLockReadFrom = true;
5823
5824 /*
5825 * Updating the cache doesn't make any sense
5826 * as we are looping once through the image.
5827 */
5828 rc = vdReadHelper(pDiskFrom, pImageFrom, NULL, uOffset, pvBuf,
5829 cbThisRead, fRegularRead,
5830 false /* fUpdateCache */);
5831 if (RT_FAILURE(rc) && rc != VERR_VD_BLOCK_FREE)
5832 break;
5833
5834 rc2 = vdThreadFinishRead(pDiskFrom);
5835 AssertRC(rc2);
5836 fLockReadFrom = false;
5837
5838 if (rc != VERR_VD_BLOCK_FREE)
5839 {
5840 rc2 = vdThreadStartWrite(pDiskTo);
5841 AssertRC(rc2);
5842 fLockWriteTo = true;
5843
5844 rc = vdWriteHelper(pDiskTo, pImageTo, NULL, uOffset, pvBuf,
5845 cbThisRead, false /* fUpdateCache */);
5846 if (RT_FAILURE(rc))
5847 break;
5848
5849 rc2 = vdThreadFinishWrite(pDiskTo);
5850 AssertRC(rc2);
5851 fLockWriteTo = false;
5852 }
5853 else /* Don't propagate the error to the outside */
5854 rc = VINF_SUCCESS;
5855
5856 uOffset += cbThisRead;
5857 cbRemaining -= cbThisRead;
5858
5859 if (pCbProgress && pCbProgress->pfnProgress)
5860 {
5861 /** @todo r=klaus: this can update the progress to the same
5862 * percentage over and over again if the image format makes
5863 * relatively small increments. */
5864 rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
5865 uOffset * 99 / cbSize);
5866 if (RT_FAILURE(rc))
5867 break;
5868 }
5869 if (pDstCbProgress && pDstCbProgress->pfnProgress)
5870 {
5871 /** @todo r=klaus: this can update the progress to the same
5872 * percentage over and over again if the image format makes
5873 * relatively small increments. */
5874 rc = pDstCbProgress->pfnProgress(pDstIfProgress->pvUser,
5875 uOffset * 99 / cbSize);
5876 if (RT_FAILURE(rc))
5877 break;
5878 }
5879 } while (uOffset < cbSize);
5880
5881 if (RT_SUCCESS(rc))
5882 {
5883 rc2 = vdThreadStartWrite(pDiskTo);
5884 AssertRC(rc2);
5885 fLockWriteTo = true;
5886
5887 /* Only set modification UUID if it is non-null, since the source
5888 * backend might not provide a valid modification UUID. */
5889 if (!RTUuidIsNull(&ImageModificationUuid))
5890 pImageTo->Backend->pfnSetModificationUuid(pImageTo->pBackendData, &ImageModificationUuid);
5891
5892 /* Set the requested open flags if they differ from the value
5893 * required for creating the image and copying the contents. */
5894 if ( pImageTo && pszFilename
5895 && uOpenFlags != (uOpenFlags & ~VD_OPEN_FLAGS_READONLY))
5896 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
5897 uOpenFlags);
5898 }
5899 } while (0);
5900
5901 if (RT_FAILURE(rc) && pImageTo && pszFilename)
5902 {
5903 /* Take the write lock only if it is not taken. Not worth making the
5904 * above code even more complicated. */
5905 if (RT_UNLIKELY(!fLockWriteTo))
5906 {
5907 rc2 = vdThreadStartWrite(pDiskTo);
5908 AssertRC(rc2);
5909 fLockWriteTo = true;
5910 }
5911 /* Error detected, but new image created. Remove image from list. */
5912 vdRemoveImageFromList(pDiskTo, pImageTo);
5913
5914 /* Close and delete image. */
5915 rc2 = pImageTo->Backend->pfnClose(pImageTo->pBackendData, true);
5916 AssertRC(rc2);
5917 pImageTo->pBackendData = NULL;
5918
5919 /* Free remaining resources. */
5920 if (pImageTo->pszFilename)
5921 RTStrFree(pImageTo->pszFilename);
5922
5923 RTMemFree(pImageTo);
5924 }
5925
5926 if (RT_UNLIKELY(fLockWriteTo))
5927 {
5928 rc2 = vdThreadFinishWrite(pDiskTo);
5929 AssertRC(rc2);
5930 }
5931 if (RT_UNLIKELY(fLockWriteFrom))
5932 {
5933 rc2 = vdThreadFinishWrite(pDiskFrom);
5934 AssertRC(rc2);
5935 }
5936 else if (RT_UNLIKELY(fLockReadFrom))
5937 {
5938 rc2 = vdThreadFinishRead(pDiskFrom);
5939 AssertRC(rc2);
5940 }
5941
5942 if (pvBuf)
5943 RTMemTmpFree(pvBuf);
5944
5945 if (RT_SUCCESS(rc))
5946 {
5947 if (pCbProgress && pCbProgress->pfnProgress)
5948 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
5949 if (pDstCbProgress && pDstCbProgress->pfnProgress)
5950 pDstCbProgress->pfnProgress(pDstIfProgress->pvUser, 100);
5951 }
5952
5953 LogFlowFunc(("returns %Rrc\n", rc));
5954 return rc;
5955}
5956
5957/**
5958 * Optimizes the storage consumption of an image. Typically the unused blocks
5959 * have to be wiped with zeroes to achieve a substantial reduced storage use.
5960 * Another optimization done is reordering the image blocks, which can provide
5961 * a significant performance boost, as reads and writes tend to use less random
5962 * file offsets.
5963 *
5964 * @return VBox status code.
5965 * @return VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
5966 * @return VERR_VD_IMAGE_READ_ONLY if image is not writable.
5967 * @return VERR_NOT_SUPPORTED if this kind of image can be compacted, but
5968 * the code for this isn't implemented yet.
5969 * @param pDisk Pointer to HDD container.
5970 * @param nImage Image number, counts from 0. 0 is always base image of container.
5971 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
5972 */
5973VBOXDDU_DECL(int) VDCompact(PVBOXHDD pDisk, unsigned nImage,
5974 PVDINTERFACE pVDIfsOperation)
5975{
5976 int rc = VINF_SUCCESS;
5977 int rc2;
5978 bool fLockRead = false, fLockWrite = false;
5979 void *pvBuf = NULL;
5980 void *pvTmp = NULL;
5981
5982 LogFlowFunc(("pDisk=%#p nImage=%u pVDIfsOperation=%#p\n",
5983 pDisk, nImage, pVDIfsOperation));
5984
5985 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
5986 VDINTERFACETYPE_PROGRESS);
5987 PVDINTERFACEPROGRESS pCbProgress = NULL;
5988 if (pIfProgress)
5989 pCbProgress = VDGetInterfaceProgress(pIfProgress);
5990
5991 do {
5992 /* Check arguments. */
5993 AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
5994 rc = VERR_INVALID_PARAMETER);
5995 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE,
5996 ("u32Signature=%08x\n", pDisk->u32Signature));
5997
5998 rc2 = vdThreadStartRead(pDisk);
5999 AssertRC(rc2);
6000 fLockRead = true;
6001
6002 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6003 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
6004
6005 /* If there is no compact callback for not file based backends then
6006 * the backend doesn't need compaction. No need to make much fuss about
6007 * this. For file based ones signal this as not yet supported. */
6008 if (!pImage->Backend->pfnCompact)
6009 {
6010 if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
6011 rc = VERR_NOT_SUPPORTED;
6012 else
6013 rc = VINF_SUCCESS;
6014 break;
6015 }
6016
6017 /* Insert interface for reading parent state into per-operation list,
6018 * if there is a parent image. */
6019 VDINTERFACE IfOpParent;
6020 VDINTERFACEPARENTSTATE ParentCb;
6021 VDPARENTSTATEDESC ParentUser;
6022 if (pImage->pPrev)
6023 {
6024 ParentCb.cbSize = sizeof(ParentCb);
6025 ParentCb.enmInterface = VDINTERFACETYPE_PARENTSTATE;
6026 ParentCb.pfnParentRead = vdParentRead;
6027 ParentUser.pDisk = pDisk;
6028 ParentUser.pImage = pImage->pPrev;
6029 rc = VDInterfaceAdd(&IfOpParent, "VDCompact_ParentState", VDINTERFACETYPE_PARENTSTATE,
6030 &ParentCb, &ParentUser, &pVDIfsOperation);
6031 AssertRC(rc);
6032 }
6033
6034 rc2 = vdThreadFinishRead(pDisk);
6035 AssertRC(rc2);
6036 fLockRead = false;
6037
6038 rc2 = vdThreadStartWrite(pDisk);
6039 AssertRC(rc2);
6040 fLockWrite = true;
6041
6042 rc = pImage->Backend->pfnCompact(pImage->pBackendData,
6043 0, 99,
6044 pDisk->pVDIfsDisk,
6045 pImage->pVDIfsImage,
6046 pVDIfsOperation);
6047 } while (0);
6048
6049 if (RT_UNLIKELY(fLockWrite))
6050 {
6051 rc2 = vdThreadFinishWrite(pDisk);
6052 AssertRC(rc2);
6053 }
6054 else if (RT_UNLIKELY(fLockRead))
6055 {
6056 rc2 = vdThreadFinishRead(pDisk);
6057 AssertRC(rc2);
6058 }
6059
6060 if (pvBuf)
6061 RTMemTmpFree(pvBuf);
6062 if (pvTmp)
6063 RTMemTmpFree(pvTmp);
6064
6065 if (RT_SUCCESS(rc))
6066 {
6067 if (pCbProgress && pCbProgress->pfnProgress)
6068 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
6069 }
6070
6071 LogFlowFunc(("returns %Rrc\n", rc));
6072 return rc;
6073}
6074
6075/**
6076 * Resizes the the given disk image to the given size.
6077 *
6078 * @return VBox status
6079 * @return VERR_VD_IMAGE_READ_ONLY if image is not writable.
6080 * @return VERR_NOT_SUPPORTED if this kind of image can be compacted, but
6081 *
6082 * @param pDisk Pointer to the HDD container.
6083 * @param cbSize New size of the image.
6084 * @param pPCHSGeometry Pointer to the new physical disk geometry <= (16383,16,63). Not NULL.
6085 * @param pLCHSGeometry Pointer to the new logical disk geometry <= (x,255,63). Not NULL.
6086 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
6087 */
6088VBOXDDU_DECL(int) VDResize(PVBOXHDD pDisk, uint64_t cbSize,
6089 PCVDGEOMETRY pPCHSGeometry,
6090 PCVDGEOMETRY pLCHSGeometry,
6091 PVDINTERFACE pVDIfsOperation)
6092{
6093 /** @todo r=klaus resizing was designed to be part of VDCopy, so having a separate function is not desirable. */
6094 int rc = VINF_SUCCESS;
6095 int rc2;
6096 bool fLockRead = false, fLockWrite = false;
6097
6098 LogFlowFunc(("pDisk=%#p cbSize=%llu pVDIfsOperation=%#p\n",
6099 pDisk, cbSize, pVDIfsOperation));
6100
6101 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
6102 VDINTERFACETYPE_PROGRESS);
6103 PVDINTERFACEPROGRESS pCbProgress = NULL;
6104 if (pIfProgress)
6105 pCbProgress = VDGetInterfaceProgress(pIfProgress);
6106
6107 do {
6108 /* Check arguments. */
6109 AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
6110 rc = VERR_INVALID_PARAMETER);
6111 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE,
6112 ("u32Signature=%08x\n", pDisk->u32Signature));
6113
6114 rc2 = vdThreadStartRead(pDisk);
6115 AssertRC(rc2);
6116 fLockRead = true;
6117
6118 /* Not supported if the disk has child images attached. */
6119 AssertMsgBreakStmt(pDisk->cImages == 1, ("cImages=%u\n", pDisk->cImages),
6120 rc = VERR_NOT_SUPPORTED);
6121
6122 PVDIMAGE pImage = pDisk->pBase;
6123
6124 /* If there is no compact callback for not file based backends then
6125 * the backend doesn't need compaction. No need to make much fuss about
6126 * this. For file based ones signal this as not yet supported. */
6127 if (!pImage->Backend->pfnResize)
6128 {
6129 if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
6130 rc = VERR_NOT_SUPPORTED;
6131 else
6132 rc = VINF_SUCCESS;
6133 break;
6134 }
6135
6136 rc2 = vdThreadFinishRead(pDisk);
6137 AssertRC(rc2);
6138 fLockRead = false;
6139
6140 rc2 = vdThreadStartWrite(pDisk);
6141 AssertRC(rc2);
6142 fLockWrite = true;
6143
6144 VDGEOMETRY PCHSGeometryOld;
6145 VDGEOMETRY LCHSGeometryOld;
6146 PCVDGEOMETRY pPCHSGeometryNew;
6147 PCVDGEOMETRY pLCHSGeometryNew;
6148
6149 if (pPCHSGeometry->cCylinders == 0)
6150 {
6151 /* Auto-detect marker, calculate new value ourself. */
6152 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData, &PCHSGeometryOld);
6153 if (RT_SUCCESS(rc) && (PCHSGeometryOld.cCylinders != 0))
6154 PCHSGeometryOld.cCylinders = RT_MIN(cbSize / 512 / PCHSGeometryOld.cHeads / PCHSGeometryOld.cSectors, 16383);
6155 else if (rc == VERR_VD_GEOMETRY_NOT_SET)
6156 rc = VINF_SUCCESS;
6157
6158 pPCHSGeometryNew = &PCHSGeometryOld;
6159 }
6160 else
6161 pPCHSGeometryNew = pPCHSGeometry;
6162
6163 if (pLCHSGeometry->cCylinders == 0)
6164 {
6165 /* Auto-detect marker, calculate new value ourself. */
6166 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData, &LCHSGeometryOld);
6167 if (RT_SUCCESS(rc) && (LCHSGeometryOld.cCylinders != 0))
6168 LCHSGeometryOld.cCylinders = cbSize / 512 / LCHSGeometryOld.cHeads / LCHSGeometryOld.cSectors;
6169 else if (rc == VERR_VD_GEOMETRY_NOT_SET)
6170 rc = VINF_SUCCESS;
6171
6172 pLCHSGeometryNew = &LCHSGeometryOld;
6173 }
6174 else
6175 pLCHSGeometryNew = pLCHSGeometry;
6176
6177 if (RT_SUCCESS(rc))
6178 rc = pImage->Backend->pfnResize(pImage->pBackendData,
6179 cbSize,
6180 pPCHSGeometryNew,
6181 pLCHSGeometryNew,
6182 0, 99,
6183 pDisk->pVDIfsDisk,
6184 pImage->pVDIfsImage,
6185 pVDIfsOperation);
6186 } while (0);
6187
6188 if (RT_UNLIKELY(fLockWrite))
6189 {
6190 rc2 = vdThreadFinishWrite(pDisk);
6191 AssertRC(rc2);
6192 }
6193 else if (RT_UNLIKELY(fLockRead))
6194 {
6195 rc2 = vdThreadFinishRead(pDisk);
6196 AssertRC(rc2);
6197 }
6198
6199 if (RT_SUCCESS(rc))
6200 {
6201 if (pCbProgress && pCbProgress->pfnProgress)
6202 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
6203 }
6204
6205 LogFlowFunc(("returns %Rrc\n", rc));
6206 return rc;
6207}
6208
6209/**
6210 * Closes the last opened image file in HDD container.
6211 * If previous image file was opened in read-only mode (the normal case) and
6212 * the last opened image is in read-write mode then the previous image will be
6213 * reopened in read/write mode.
6214 *
6215 * @returns VBox status code.
6216 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
6217 * @param pDisk Pointer to HDD container.
6218 * @param fDelete If true, delete the image from the host disk.
6219 */
6220VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete)
6221{
6222 int rc = VINF_SUCCESS;
6223 int rc2;
6224 bool fLockWrite = false;
6225
6226 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
6227 do
6228 {
6229 /* sanity check */
6230 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6231 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6232
6233 /* Not worth splitting this up into a read lock phase and write
6234 * lock phase, as closing an image is a relatively fast operation
6235 * dominated by the part which needs the write lock. */
6236 rc2 = vdThreadStartWrite(pDisk);
6237 AssertRC(rc2);
6238 fLockWrite = true;
6239
6240 PVDIMAGE pImage = pDisk->pLast;
6241 if (!pImage)
6242 {
6243 rc = VERR_VD_NOT_OPENED;
6244 break;
6245 }
6246 unsigned uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
6247 /* Remove image from list of opened images. */
6248 vdRemoveImageFromList(pDisk, pImage);
6249 /* Close (and optionally delete) image. */
6250 rc = pImage->Backend->pfnClose(pImage->pBackendData, fDelete);
6251 /* Free remaining resources related to the image. */
6252 RTStrFree(pImage->pszFilename);
6253 RTMemFree(pImage);
6254
6255 pImage = pDisk->pLast;
6256 if (!pImage)
6257 break;
6258
6259 /* If disk was previously in read/write mode, make sure it will stay
6260 * like this (if possible) after closing this image. Set the open flags
6261 * accordingly. */
6262 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
6263 {
6264 uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
6265 uOpenFlags &= ~ VD_OPEN_FLAGS_READONLY;
6266 rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData, uOpenFlags);
6267 }
6268
6269 /* Cache disk information. */
6270 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
6271
6272 /* Cache PCHS geometry. */
6273 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
6274 &pDisk->PCHSGeometry);
6275 if (RT_FAILURE(rc2))
6276 {
6277 pDisk->PCHSGeometry.cCylinders = 0;
6278 pDisk->PCHSGeometry.cHeads = 0;
6279 pDisk->PCHSGeometry.cSectors = 0;
6280 }
6281 else
6282 {
6283 /* Make sure the PCHS geometry is properly clipped. */
6284 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
6285 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
6286 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
6287 }
6288
6289 /* Cache LCHS geometry. */
6290 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
6291 &pDisk->LCHSGeometry);
6292 if (RT_FAILURE(rc2))
6293 {
6294 pDisk->LCHSGeometry.cCylinders = 0;
6295 pDisk->LCHSGeometry.cHeads = 0;
6296 pDisk->LCHSGeometry.cSectors = 0;
6297 }
6298 else
6299 {
6300 /* Make sure the LCHS geometry is properly clipped. */
6301 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
6302 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
6303 }
6304 } while (0);
6305
6306 if (RT_UNLIKELY(fLockWrite))
6307 {
6308 rc2 = vdThreadFinishWrite(pDisk);
6309 AssertRC(rc2);
6310 }
6311
6312 LogFlowFunc(("returns %Rrc\n", rc));
6313 return rc;
6314}
6315
6316/**
6317 * Closes the currently opened cache image file in HDD container.
6318 *
6319 * @return VBox status code.
6320 * @return VERR_VD_NOT_OPENED if no cache is opened in HDD container.
6321 * @param pDisk Pointer to HDD container.
6322 * @param fDelete If true, delete the image from the host disk.
6323 */
6324VBOXDDU_DECL(int) VDCacheClose(PVBOXHDD pDisk, bool fDelete)
6325{
6326 int rc = VINF_SUCCESS;
6327 int rc2;
6328 bool fLockWrite = false;
6329 PVDCACHE pCache = NULL;
6330
6331 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
6332
6333 do
6334 {
6335 /* sanity check */
6336 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6337 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6338
6339 rc2 = vdThreadStartWrite(pDisk);
6340 AssertRC(rc2);
6341 fLockWrite = true;
6342
6343 AssertPtrBreakStmt(pDisk->pCache, rc = VERR_VD_CACHE_NOT_FOUND);
6344
6345 pCache = pDisk->pCache;
6346 pDisk->pCache = NULL;
6347
6348 pCache->Backend->pfnClose(pCache->pBackendData, fDelete);
6349 if (pCache->pszFilename)
6350 RTStrFree(pCache->pszFilename);
6351 RTMemFree(pCache);
6352 } while (0);
6353
6354 if (RT_LIKELY(fLockWrite))
6355 {
6356 rc2 = vdThreadFinishWrite(pDisk);
6357 AssertRC(rc2);
6358 }
6359
6360 LogFlowFunc(("returns %Rrc\n", rc));
6361 return rc;
6362}
6363
6364/**
6365 * Closes all opened image files in HDD container.
6366 *
6367 * @returns VBox status code.
6368 * @param pDisk Pointer to HDD container.
6369 */
6370VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk)
6371{
6372 int rc = VINF_SUCCESS;
6373 int rc2;
6374 bool fLockWrite = false;
6375
6376 LogFlowFunc(("pDisk=%#p\n", pDisk));
6377 do
6378 {
6379 /* sanity check */
6380 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6381 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6382
6383 /* Lock the entire operation. */
6384 rc2 = vdThreadStartWrite(pDisk);
6385 AssertRC(rc2);
6386 fLockWrite = true;
6387
6388 PVDCACHE pCache = pDisk->pCache;
6389 if (pCache)
6390 {
6391 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, false);
6392 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
6393 rc = rc2;
6394
6395 if (pCache->pszFilename)
6396 RTStrFree(pCache->pszFilename);
6397 RTMemFree(pCache);
6398 }
6399
6400 PVDIMAGE pImage = pDisk->pLast;
6401 while (VALID_PTR(pImage))
6402 {
6403 PVDIMAGE pPrev = pImage->pPrev;
6404 /* Remove image from list of opened images. */
6405 vdRemoveImageFromList(pDisk, pImage);
6406 /* Close image. */
6407 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, false);
6408 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
6409 rc = rc2;
6410 /* Free remaining resources related to the image. */
6411 RTStrFree(pImage->pszFilename);
6412 RTMemFree(pImage);
6413 pImage = pPrev;
6414 }
6415 Assert(!VALID_PTR(pDisk->pLast));
6416 } while (0);
6417
6418 if (RT_UNLIKELY(fLockWrite))
6419 {
6420 rc2 = vdThreadFinishWrite(pDisk);
6421 AssertRC(rc2);
6422 }
6423
6424 LogFlowFunc(("returns %Rrc\n", rc));
6425 return rc;
6426}
6427
6428/**
6429 * Read data from virtual HDD.
6430 *
6431 * @returns VBox status code.
6432 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
6433 * @param pDisk Pointer to HDD container.
6434 * @param uOffset Offset of first reading byte from start of disk.
6435 * @param pvBuf Pointer to buffer for reading data.
6436 * @param cbRead Number of bytes to read.
6437 */
6438VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuf,
6439 size_t cbRead)
6440{
6441 int rc = VINF_SUCCESS;
6442 int rc2;
6443 bool fLockRead = false;
6444
6445 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbRead=%zu\n",
6446 pDisk, uOffset, pvBuf, cbRead));
6447 do
6448 {
6449 /* sanity check */
6450 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6451 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6452
6453 /* Check arguments. */
6454 AssertMsgBreakStmt(VALID_PTR(pvBuf),
6455 ("pvBuf=%#p\n", pvBuf),
6456 rc = VERR_INVALID_PARAMETER);
6457 AssertMsgBreakStmt(cbRead,
6458 ("cbRead=%zu\n", cbRead),
6459 rc = VERR_INVALID_PARAMETER);
6460
6461 rc2 = vdThreadStartRead(pDisk);
6462 AssertRC(rc2);
6463 fLockRead = true;
6464
6465 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
6466 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
6467 uOffset, cbRead, pDisk->cbSize),
6468 rc = VERR_INVALID_PARAMETER);
6469
6470 PVDIMAGE pImage = pDisk->pLast;
6471 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
6472
6473 rc = vdReadHelper(pDisk, pImage, NULL, uOffset, pvBuf, cbRead,
6474 true /* fZeroFreeBlocks */,
6475 true /* fUpdateCache */);
6476 } while (0);
6477
6478 if (RT_UNLIKELY(fLockRead))
6479 {
6480 rc2 = vdThreadFinishRead(pDisk);
6481 AssertRC(rc2);
6482 }
6483
6484 LogFlowFunc(("returns %Rrc\n", rc));
6485 return rc;
6486}
6487
6488/**
6489 * Write data to virtual HDD.
6490 *
6491 * @returns VBox status code.
6492 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
6493 * @param pDisk Pointer to HDD container.
6494 * @param uOffset Offset of the first byte being
6495 * written from start of disk.
6496 * @param pvBuf Pointer to buffer for writing data.
6497 * @param cbWrite Number of bytes to write.
6498 */
6499VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf,
6500 size_t cbWrite)
6501{
6502 int rc = VINF_SUCCESS;
6503 int rc2;
6504 bool fLockWrite = false;
6505
6506 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbWrite=%zu\n",
6507 pDisk, uOffset, pvBuf, cbWrite));
6508 do
6509 {
6510 /* sanity check */
6511 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6512 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6513
6514 /* Check arguments. */
6515 AssertMsgBreakStmt(VALID_PTR(pvBuf),
6516 ("pvBuf=%#p\n", pvBuf),
6517 rc = VERR_INVALID_PARAMETER);
6518 AssertMsgBreakStmt(cbWrite,
6519 ("cbWrite=%zu\n", cbWrite),
6520 rc = VERR_INVALID_PARAMETER);
6521
6522 rc2 = vdThreadStartWrite(pDisk);
6523 AssertRC(rc2);
6524 fLockWrite = true;
6525
6526 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
6527 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
6528 uOffset, cbWrite, pDisk->cbSize),
6529 rc = VERR_INVALID_PARAMETER);
6530
6531 PVDIMAGE pImage = pDisk->pLast;
6532 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
6533
6534 vdSetModifiedFlag(pDisk);
6535 rc = vdWriteHelper(pDisk, pImage, NULL, uOffset, pvBuf, cbWrite,
6536 true /* fUpdateCache */);
6537 if (RT_FAILURE(rc))
6538 break;
6539
6540 /* If there is a merge (in the direction towards a parent) running
6541 * concurrently then we have to also "relay" the write to this parent,
6542 * as the merge position might be already past the position where
6543 * this write is going. The "context" of the write can come from the
6544 * natural chain, since merging either already did or will take care
6545 * of the "other" content which is might be needed to fill the block
6546 * to a full allocation size. The cache doesn't need to be touched
6547 * as this write is covered by the previous one. */
6548 if (RT_UNLIKELY(pDisk->pImageRelay))
6549 rc = vdWriteHelper(pDisk, pDisk->pImageRelay, NULL, uOffset,
6550 pvBuf, cbWrite, false /* fUpdateCache */);
6551 } while (0);
6552
6553 if (RT_UNLIKELY(fLockWrite))
6554 {
6555 rc2 = vdThreadFinishWrite(pDisk);
6556 AssertRC(rc2);
6557 }
6558
6559 LogFlowFunc(("returns %Rrc\n", rc));
6560 return rc;
6561}
6562
6563/**
6564 * Make sure the on disk representation of a virtual HDD is up to date.
6565 *
6566 * @returns VBox status code.
6567 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
6568 * @param pDisk Pointer to HDD container.
6569 */
6570VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
6571{
6572 int rc = VINF_SUCCESS;
6573 int rc2;
6574 bool fLockWrite = false;
6575
6576 LogFlowFunc(("pDisk=%#p\n", pDisk));
6577 do
6578 {
6579 /* sanity check */
6580 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6581 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6582
6583 rc2 = vdThreadStartWrite(pDisk);
6584 AssertRC(rc2);
6585 fLockWrite = true;
6586
6587 PVDIMAGE pImage = pDisk->pLast;
6588 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
6589
6590 vdResetModifiedFlag(pDisk);
6591 rc = pImage->Backend->pfnFlush(pImage->pBackendData);
6592
6593 if ( RT_SUCCESS(rc)
6594 && pDisk->pCache)
6595 rc = pDisk->pCache->Backend->pfnFlush(pDisk->pCache->pBackendData);
6596 } while (0);
6597
6598 if (RT_UNLIKELY(fLockWrite))
6599 {
6600 rc2 = vdThreadFinishWrite(pDisk);
6601 AssertRC(rc2);
6602 }
6603
6604 LogFlowFunc(("returns %Rrc\n", rc));
6605 return rc;
6606}
6607
6608/**
6609 * Get number of opened images in HDD container.
6610 *
6611 * @returns Number of opened images for HDD container. 0 if no images have been opened.
6612 * @param pDisk Pointer to HDD container.
6613 */
6614VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk)
6615{
6616 unsigned cImages;
6617 int rc2;
6618 bool fLockRead = false;
6619
6620 LogFlowFunc(("pDisk=%#p\n", pDisk));
6621 do
6622 {
6623 /* sanity check */
6624 AssertPtrBreakStmt(pDisk, cImages = 0);
6625 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6626
6627 rc2 = vdThreadStartRead(pDisk);
6628 AssertRC(rc2);
6629 fLockRead = true;
6630
6631 cImages = pDisk->cImages;
6632 } while (0);
6633
6634 if (RT_UNLIKELY(fLockRead))
6635 {
6636 rc2 = vdThreadFinishRead(pDisk);
6637 AssertRC(rc2);
6638 }
6639
6640 LogFlowFunc(("returns %u\n", cImages));
6641 return cImages;
6642}
6643
6644/**
6645 * Get read/write mode of HDD container.
6646 *
6647 * @returns Virtual disk ReadOnly status.
6648 * @returns true if no image is opened in HDD container.
6649 * @param pDisk Pointer to HDD container.
6650 */
6651VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
6652{
6653 bool fReadOnly;
6654 int rc2;
6655 bool fLockRead = false;
6656
6657 LogFlowFunc(("pDisk=%#p\n", pDisk));
6658 do
6659 {
6660 /* sanity check */
6661 AssertPtrBreakStmt(pDisk, fReadOnly = false);
6662 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6663
6664 rc2 = vdThreadStartRead(pDisk);
6665 AssertRC(rc2);
6666 fLockRead = true;
6667
6668 PVDIMAGE pImage = pDisk->pLast;
6669 AssertPtrBreakStmt(pImage, fReadOnly = true);
6670
6671 unsigned uOpenFlags;
6672 uOpenFlags = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
6673 fReadOnly = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
6674 } while (0);
6675
6676 if (RT_UNLIKELY(fLockRead))
6677 {
6678 rc2 = vdThreadFinishRead(pDisk);
6679 AssertRC(rc2);
6680 }
6681
6682 LogFlowFunc(("returns %d\n", fReadOnly));
6683 return fReadOnly;
6684}
6685
6686/**
6687 * Get total capacity of an image in HDD container.
6688 *
6689 * @returns Virtual disk size in bytes.
6690 * @returns 0 if no image with specified number was not opened.
6691 * @param pDisk Pointer to HDD container.
6692 * @param nImage Image number, counts from 0. 0 is always base image of container.
6693 */
6694VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk, unsigned nImage)
6695{
6696 uint64_t cbSize;
6697 int rc2;
6698 bool fLockRead = false;
6699
6700 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
6701 do
6702 {
6703 /* sanity check */
6704 AssertPtrBreakStmt(pDisk, cbSize = 0);
6705 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6706
6707 rc2 = vdThreadStartRead(pDisk);
6708 AssertRC(rc2);
6709 fLockRead = true;
6710
6711 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6712 AssertPtrBreakStmt(pImage, cbSize = 0);
6713 cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
6714 } while (0);
6715
6716 if (RT_UNLIKELY(fLockRead))
6717 {
6718 rc2 = vdThreadFinishRead(pDisk);
6719 AssertRC(rc2);
6720 }
6721
6722 LogFlowFunc(("returns %llu\n", cbSize));
6723 return cbSize;
6724}
6725
6726/**
6727 * Get total file size of an image in HDD container.
6728 *
6729 * @returns Virtual disk size in bytes.
6730 * @returns 0 if no image is opened in HDD container.
6731 * @param pDisk Pointer to HDD container.
6732 * @param nImage Image number, counts from 0. 0 is always base image of container.
6733 */
6734VBOXDDU_DECL(uint64_t) VDGetFileSize(PVBOXHDD pDisk, unsigned nImage)
6735{
6736 uint64_t cbSize;
6737 int rc2;
6738 bool fLockRead = false;
6739
6740 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
6741 do
6742 {
6743 /* sanity check */
6744 AssertPtrBreakStmt(pDisk, cbSize = 0);
6745 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6746
6747 rc2 = vdThreadStartRead(pDisk);
6748 AssertRC(rc2);
6749 fLockRead = true;
6750
6751 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6752 AssertPtrBreakStmt(pImage, cbSize = 0);
6753 cbSize = pImage->Backend->pfnGetFileSize(pImage->pBackendData);
6754 } while (0);
6755
6756 if (RT_UNLIKELY(fLockRead))
6757 {
6758 rc2 = vdThreadFinishRead(pDisk);
6759 AssertRC(rc2);
6760 }
6761
6762 LogFlowFunc(("returns %llu\n", cbSize));
6763 return cbSize;
6764}
6765
6766/**
6767 * Get virtual disk PCHS geometry stored in HDD container.
6768 *
6769 * @returns VBox status code.
6770 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
6771 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
6772 * @param pDisk Pointer to HDD container.
6773 * @param nImage Image number, counts from 0. 0 is always base image of container.
6774 * @param pPCHSGeometry Where to store PCHS geometry. Not NULL.
6775 */
6776VBOXDDU_DECL(int) VDGetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
6777 PVDGEOMETRY pPCHSGeometry)
6778{
6779 int rc = VINF_SUCCESS;
6780 int rc2;
6781 bool fLockRead = false;
6782
6783 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p\n",
6784 pDisk, nImage, pPCHSGeometry));
6785 do
6786 {
6787 /* sanity check */
6788 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6789 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6790
6791 /* Check arguments. */
6792 AssertMsgBreakStmt(VALID_PTR(pPCHSGeometry),
6793 ("pPCHSGeometry=%#p\n", pPCHSGeometry),
6794 rc = VERR_INVALID_PARAMETER);
6795
6796 rc2 = vdThreadStartRead(pDisk);
6797 AssertRC(rc2);
6798 fLockRead = true;
6799
6800 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6801 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
6802
6803 if (pImage == pDisk->pLast)
6804 {
6805 /* Use cached information if possible. */
6806 if (pDisk->PCHSGeometry.cCylinders != 0)
6807 *pPCHSGeometry = pDisk->PCHSGeometry;
6808 else
6809 rc = VERR_VD_GEOMETRY_NOT_SET;
6810 }
6811 else
6812 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
6813 pPCHSGeometry);
6814 } while (0);
6815
6816 if (RT_UNLIKELY(fLockRead))
6817 {
6818 rc2 = vdThreadFinishRead(pDisk);
6819 AssertRC(rc2);
6820 }
6821
6822 LogFlowFunc(("%Rrc (PCHS=%u/%u/%u)\n", rc,
6823 pDisk->PCHSGeometry.cCylinders, pDisk->PCHSGeometry.cHeads,
6824 pDisk->PCHSGeometry.cSectors));
6825 return rc;
6826}
6827
6828/**
6829 * Store virtual disk PCHS geometry in HDD container.
6830 *
6831 * Note that in case of unrecoverable error all images in HDD container will be closed.
6832 *
6833 * @returns VBox status code.
6834 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
6835 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
6836 * @param pDisk Pointer to HDD container.
6837 * @param nImage Image number, counts from 0. 0 is always base image of container.
6838 * @param pPCHSGeometry Where to load PCHS geometry from. Not NULL.
6839 */
6840VBOXDDU_DECL(int) VDSetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
6841 PCVDGEOMETRY pPCHSGeometry)
6842{
6843 int rc = VINF_SUCCESS;
6844 int rc2;
6845 bool fLockWrite = false;
6846
6847 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
6848 pDisk, nImage, pPCHSGeometry, pPCHSGeometry->cCylinders,
6849 pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
6850 do
6851 {
6852 /* sanity check */
6853 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6854 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6855
6856 /* Check arguments. */
6857 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
6858 && pPCHSGeometry->cHeads <= 16
6859 && pPCHSGeometry->cSectors <= 63,
6860 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
6861 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
6862 pPCHSGeometry->cSectors),
6863 rc = VERR_INVALID_PARAMETER);
6864
6865 rc2 = vdThreadStartWrite(pDisk);
6866 AssertRC(rc2);
6867 fLockWrite = true;
6868
6869 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6870 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
6871
6872 if (pImage == pDisk->pLast)
6873 {
6874 if ( pPCHSGeometry->cCylinders != pDisk->PCHSGeometry.cCylinders
6875 || pPCHSGeometry->cHeads != pDisk->PCHSGeometry.cHeads
6876 || pPCHSGeometry->cSectors != pDisk->PCHSGeometry.cSectors)
6877 {
6878 /* Only update geometry if it is changed. Avoids similar checks
6879 * in every backend. Most of the time the new geometry is set
6880 * to the previous values, so no need to go through the hassle
6881 * of updating an image which could be opened in read-only mode
6882 * right now. */
6883 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pBackendData,
6884 pPCHSGeometry);
6885
6886 /* Cache new geometry values in any case. */
6887 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
6888 &pDisk->PCHSGeometry);
6889 if (RT_FAILURE(rc2))
6890 {
6891 pDisk->PCHSGeometry.cCylinders = 0;
6892 pDisk->PCHSGeometry.cHeads = 0;
6893 pDisk->PCHSGeometry.cSectors = 0;
6894 }
6895 else
6896 {
6897 /* Make sure the CHS geometry is properly clipped. */
6898 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 255);
6899 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
6900 }
6901 }
6902 }
6903 else
6904 {
6905 VDGEOMETRY PCHS;
6906 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
6907 &PCHS);
6908 if ( RT_FAILURE(rc)
6909 || pPCHSGeometry->cCylinders != PCHS.cCylinders
6910 || pPCHSGeometry->cHeads != PCHS.cHeads
6911 || pPCHSGeometry->cSectors != PCHS.cSectors)
6912 {
6913 /* Only update geometry if it is changed. Avoids similar checks
6914 * in every backend. Most of the time the new geometry is set
6915 * to the previous values, so no need to go through the hassle
6916 * of updating an image which could be opened in read-only mode
6917 * right now. */
6918 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pBackendData,
6919 pPCHSGeometry);
6920 }
6921 }
6922 } while (0);
6923
6924 if (RT_UNLIKELY(fLockWrite))
6925 {
6926 rc2 = vdThreadFinishWrite(pDisk);
6927 AssertRC(rc2);
6928 }
6929
6930 LogFlowFunc(("returns %Rrc\n", rc));
6931 return rc;
6932}
6933
6934/**
6935 * Get virtual disk LCHS geometry stored in HDD container.
6936 *
6937 * @returns VBox status code.
6938 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
6939 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
6940 * @param pDisk Pointer to HDD container.
6941 * @param nImage Image number, counts from 0. 0 is always base image of container.
6942 * @param pLCHSGeometry Where to store LCHS geometry. Not NULL.
6943 */
6944VBOXDDU_DECL(int) VDGetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
6945 PVDGEOMETRY pLCHSGeometry)
6946{
6947 int rc = VINF_SUCCESS;
6948 int rc2;
6949 bool fLockRead = false;
6950
6951 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p\n",
6952 pDisk, nImage, pLCHSGeometry));
6953 do
6954 {
6955 /* sanity check */
6956 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6957 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6958
6959 /* Check arguments. */
6960 AssertMsgBreakStmt(VALID_PTR(pLCHSGeometry),
6961 ("pLCHSGeometry=%#p\n", pLCHSGeometry),
6962 rc = VERR_INVALID_PARAMETER);
6963
6964 rc2 = vdThreadStartRead(pDisk);
6965 AssertRC(rc2);
6966 fLockRead = true;
6967
6968 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
6969 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
6970
6971 if (pImage == pDisk->pLast)
6972 {
6973 /* Use cached information if possible. */
6974 if (pDisk->LCHSGeometry.cCylinders != 0)
6975 *pLCHSGeometry = pDisk->LCHSGeometry;
6976 else
6977 rc = VERR_VD_GEOMETRY_NOT_SET;
6978 }
6979 else
6980 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
6981 pLCHSGeometry);
6982 } while (0);
6983
6984 if (RT_UNLIKELY(fLockRead))
6985 {
6986 rc2 = vdThreadFinishRead(pDisk);
6987 AssertRC(rc2);
6988 }
6989
6990 LogFlowFunc((": %Rrc (LCHS=%u/%u/%u)\n", rc,
6991 pDisk->LCHSGeometry.cCylinders, pDisk->LCHSGeometry.cHeads,
6992 pDisk->LCHSGeometry.cSectors));
6993 return rc;
6994}
6995
6996/**
6997 * Store virtual disk LCHS geometry in HDD container.
6998 *
6999 * Note that in case of unrecoverable error all images in HDD container will be closed.
7000 *
7001 * @returns VBox status code.
7002 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7003 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
7004 * @param pDisk Pointer to HDD container.
7005 * @param nImage Image number, counts from 0. 0 is always base image of container.
7006 * @param pLCHSGeometry Where to load LCHS geometry from. Not NULL.
7007 */
7008VBOXDDU_DECL(int) VDSetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
7009 PCVDGEOMETRY pLCHSGeometry)
7010{
7011 int rc = VINF_SUCCESS;
7012 int rc2;
7013 bool fLockWrite = false;
7014
7015 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
7016 pDisk, nImage, pLCHSGeometry, pLCHSGeometry->cCylinders,
7017 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
7018 do
7019 {
7020 /* sanity check */
7021 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7022 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7023
7024 /* Check arguments. */
7025 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
7026 && pLCHSGeometry->cHeads <= 255
7027 && pLCHSGeometry->cSectors <= 63,
7028 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
7029 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
7030 pLCHSGeometry->cSectors),
7031 rc = VERR_INVALID_PARAMETER);
7032
7033 rc2 = vdThreadStartWrite(pDisk);
7034 AssertRC(rc2);
7035 fLockWrite = true;
7036
7037 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7038 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7039
7040 if (pImage == pDisk->pLast)
7041 {
7042 if ( pLCHSGeometry->cCylinders != pDisk->LCHSGeometry.cCylinders
7043 || pLCHSGeometry->cHeads != pDisk->LCHSGeometry.cHeads
7044 || pLCHSGeometry->cSectors != pDisk->LCHSGeometry.cSectors)
7045 {
7046 /* Only update geometry if it is changed. Avoids similar checks
7047 * in every backend. Most of the time the new geometry is set
7048 * to the previous values, so no need to go through the hassle
7049 * of updating an image which could be opened in read-only mode
7050 * right now. */
7051 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pBackendData,
7052 pLCHSGeometry);
7053
7054 /* Cache new geometry values in any case. */
7055 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
7056 &pDisk->LCHSGeometry);
7057 if (RT_FAILURE(rc2))
7058 {
7059 pDisk->LCHSGeometry.cCylinders = 0;
7060 pDisk->LCHSGeometry.cHeads = 0;
7061 pDisk->LCHSGeometry.cSectors = 0;
7062 }
7063 else
7064 {
7065 /* Make sure the CHS geometry is properly clipped. */
7066 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
7067 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
7068 }
7069 }
7070 }
7071 else
7072 {
7073 VDGEOMETRY LCHS;
7074 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
7075 &LCHS);
7076 if ( RT_FAILURE(rc)
7077 || pLCHSGeometry->cCylinders != LCHS.cCylinders
7078 || pLCHSGeometry->cHeads != LCHS.cHeads
7079 || pLCHSGeometry->cSectors != LCHS.cSectors)
7080 {
7081 /* Only update geometry if it is changed. Avoids similar checks
7082 * in every backend. Most of the time the new geometry is set
7083 * to the previous values, so no need to go through the hassle
7084 * of updating an image which could be opened in read-only mode
7085 * right now. */
7086 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pBackendData,
7087 pLCHSGeometry);
7088 }
7089 }
7090 } while (0);
7091
7092 if (RT_UNLIKELY(fLockWrite))
7093 {
7094 rc2 = vdThreadFinishWrite(pDisk);
7095 AssertRC(rc2);
7096 }
7097
7098 LogFlowFunc(("returns %Rrc\n", rc));
7099 return rc;
7100}
7101
7102/**
7103 * Get version of image in HDD container.
7104 *
7105 * @returns VBox status code.
7106 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7107 * @param pDisk Pointer to HDD container.
7108 * @param nImage Image number, counts from 0. 0 is always base image of container.
7109 * @param puVersion Where to store the image version.
7110 */
7111VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
7112 unsigned *puVersion)
7113{
7114 int rc = VINF_SUCCESS;
7115 int rc2;
7116 bool fLockRead = false;
7117
7118 LogFlowFunc(("pDisk=%#p nImage=%u puVersion=%#p\n",
7119 pDisk, nImage, puVersion));
7120 do
7121 {
7122 /* sanity check */
7123 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7124 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7125
7126 /* Check arguments. */
7127 AssertMsgBreakStmt(VALID_PTR(puVersion),
7128 ("puVersion=%#p\n", puVersion),
7129 rc = VERR_INVALID_PARAMETER);
7130
7131 rc2 = vdThreadStartRead(pDisk);
7132 AssertRC(rc2);
7133 fLockRead = true;
7134
7135 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7136 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7137
7138 *puVersion = pImage->Backend->pfnGetVersion(pImage->pBackendData);
7139 } while (0);
7140
7141 if (RT_UNLIKELY(fLockRead))
7142 {
7143 rc2 = vdThreadFinishRead(pDisk);
7144 AssertRC(rc2);
7145 }
7146
7147 LogFlowFunc(("returns %Rrc uVersion=%#x\n", rc, *puVersion));
7148 return rc;
7149}
7150
7151/**
7152 * List the capabilities of image backend in HDD container.
7153 *
7154 * @returns VBox status code.
7155 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7156 * @param pDisk Pointer to the HDD container.
7157 * @param nImage Image number, counts from 0. 0 is always base image of container.
7158 * @param pbackendInfo Where to store the backend information.
7159 */
7160VBOXDDU_DECL(int) VDBackendInfoSingle(PVBOXHDD pDisk, unsigned nImage,
7161 PVDBACKENDINFO pBackendInfo)
7162{
7163 int rc = VINF_SUCCESS;
7164 int rc2;
7165 bool fLockRead = false;
7166
7167 LogFlowFunc(("pDisk=%#p nImage=%u pBackendInfo=%#p\n",
7168 pDisk, nImage, pBackendInfo));
7169 do
7170 {
7171 /* sanity check */
7172 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7173 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7174
7175 /* Check arguments. */
7176 AssertMsgBreakStmt(VALID_PTR(pBackendInfo),
7177 ("pBackendInfo=%#p\n", pBackendInfo),
7178 rc = VERR_INVALID_PARAMETER);
7179
7180 rc2 = vdThreadStartRead(pDisk);
7181 AssertRC(rc2);
7182 fLockRead = true;
7183
7184 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7185 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7186
7187 pBackendInfo->pszBackend = pImage->Backend->pszBackendName;
7188 pBackendInfo->uBackendCaps = pImage->Backend->uBackendCaps;
7189 pBackendInfo->paFileExtensions = pImage->Backend->paFileExtensions;
7190 pBackendInfo->paConfigInfo = pImage->Backend->paConfigInfo;
7191 } while (0);
7192
7193 if (RT_UNLIKELY(fLockRead))
7194 {
7195 rc2 = vdThreadFinishRead(pDisk);
7196 AssertRC(rc2);
7197 }
7198
7199 LogFlowFunc(("returns %Rrc\n", rc));
7200 return rc;
7201}
7202
7203/**
7204 * Get flags of image in HDD container.
7205 *
7206 * @returns VBox status code.
7207 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7208 * @param pDisk Pointer to HDD container.
7209 * @param nImage Image number, counts from 0. 0 is always base image of container.
7210 * @param puImageFlags Where to store the image flags.
7211 */
7212VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage,
7213 unsigned *puImageFlags)
7214{
7215 int rc = VINF_SUCCESS;
7216 int rc2;
7217 bool fLockRead = false;
7218
7219 LogFlowFunc(("pDisk=%#p nImage=%u puImageFlags=%#p\n",
7220 pDisk, nImage, puImageFlags));
7221 do
7222 {
7223 /* sanity check */
7224 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7225 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7226
7227 /* Check arguments. */
7228 AssertMsgBreakStmt(VALID_PTR(puImageFlags),
7229 ("puImageFlags=%#p\n", puImageFlags),
7230 rc = VERR_INVALID_PARAMETER);
7231
7232 rc2 = vdThreadStartRead(pDisk);
7233 AssertRC(rc2);
7234 fLockRead = true;
7235
7236 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7237 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7238
7239 *puImageFlags = pImage->uImageFlags;
7240 } while (0);
7241
7242 if (RT_UNLIKELY(fLockRead))
7243 {
7244 rc2 = vdThreadFinishRead(pDisk);
7245 AssertRC(rc2);
7246 }
7247
7248 LogFlowFunc(("returns %Rrc uImageFlags=%#x\n", rc, *puImageFlags));
7249 return rc;
7250}
7251
7252/**
7253 * Get open flags of image in HDD container.
7254 *
7255 * @returns VBox status code.
7256 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7257 * @param pDisk Pointer to HDD container.
7258 * @param nImage Image number, counts from 0. 0 is always base image of container.
7259 * @param puOpenFlags Where to store the image open flags.
7260 */
7261VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
7262 unsigned *puOpenFlags)
7263{
7264 int rc = VINF_SUCCESS;
7265 int rc2;
7266 bool fLockRead = false;
7267
7268 LogFlowFunc(("pDisk=%#p nImage=%u puOpenFlags=%#p\n",
7269 pDisk, nImage, puOpenFlags));
7270 do
7271 {
7272 /* sanity check */
7273 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7274 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7275
7276 /* Check arguments. */
7277 AssertMsgBreakStmt(VALID_PTR(puOpenFlags),
7278 ("puOpenFlags=%#p\n", puOpenFlags),
7279 rc = VERR_INVALID_PARAMETER);
7280
7281 rc2 = vdThreadStartRead(pDisk);
7282 AssertRC(rc2);
7283 fLockRead = true;
7284
7285 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7286 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7287
7288 *puOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
7289 } while (0);
7290
7291 if (RT_UNLIKELY(fLockRead))
7292 {
7293 rc2 = vdThreadFinishRead(pDisk);
7294 AssertRC(rc2);
7295 }
7296
7297 LogFlowFunc(("returns %Rrc uOpenFlags=%#x\n", rc, *puOpenFlags));
7298 return rc;
7299}
7300
7301/**
7302 * Set open flags of image in HDD container.
7303 * This operation may cause file locking changes and/or files being reopened.
7304 * Note that in case of unrecoverable error all images in HDD container will be closed.
7305 *
7306 * @returns VBox status code.
7307 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7308 * @param pDisk Pointer to HDD container.
7309 * @param nImage Image number, counts from 0. 0 is always base image of container.
7310 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
7311 */
7312VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
7313 unsigned uOpenFlags)
7314{
7315 int rc;
7316 int rc2;
7317 bool fLockWrite = false;
7318
7319 LogFlowFunc(("pDisk=%#p uOpenFlags=%#u\n", pDisk, uOpenFlags));
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((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
7328 ("uOpenFlags=%#x\n", uOpenFlags),
7329 rc = VERR_INVALID_PARAMETER);
7330
7331 rc2 = vdThreadStartWrite(pDisk);
7332 AssertRC(rc2);
7333 fLockWrite = true;
7334
7335 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7336 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7337
7338 rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData,
7339 uOpenFlags);
7340 } while (0);
7341
7342 if (RT_UNLIKELY(fLockWrite))
7343 {
7344 rc2 = vdThreadFinishWrite(pDisk);
7345 AssertRC(rc2);
7346 }
7347
7348 LogFlowFunc(("returns %Rrc\n", rc));
7349 return rc;
7350}
7351
7352/**
7353 * Get base filename of image in HDD container. Some image formats use
7354 * other filenames as well, so don't use this for anything but informational
7355 * purposes.
7356 *
7357 * @returns VBox status code.
7358 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7359 * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
7360 * @param pDisk Pointer to HDD container.
7361 * @param nImage Image number, counts from 0. 0 is always base image of container.
7362 * @param pszFilename Where to store the image file name.
7363 * @param cbFilename Size of buffer pszFilename points to.
7364 */
7365VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
7366 char *pszFilename, unsigned cbFilename)
7367{
7368 int rc;
7369 int rc2;
7370 bool fLockRead = false;
7371
7372 LogFlowFunc(("pDisk=%#p nImage=%u pszFilename=%#p cbFilename=%u\n",
7373 pDisk, nImage, pszFilename, cbFilename));
7374 do
7375 {
7376 /* sanity check */
7377 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7378 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7379
7380 /* Check arguments. */
7381 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
7382 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
7383 rc = VERR_INVALID_PARAMETER);
7384 AssertMsgBreakStmt(cbFilename,
7385 ("cbFilename=%u\n", cbFilename),
7386 rc = VERR_INVALID_PARAMETER);
7387
7388 rc2 = vdThreadStartRead(pDisk);
7389 AssertRC(rc2);
7390 fLockRead = true;
7391
7392 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7393 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7394
7395 size_t cb = strlen(pImage->pszFilename);
7396 if (cb <= cbFilename)
7397 {
7398 strcpy(pszFilename, pImage->pszFilename);
7399 rc = VINF_SUCCESS;
7400 }
7401 else
7402 {
7403 strncpy(pszFilename, pImage->pszFilename, cbFilename - 1);
7404 pszFilename[cbFilename - 1] = '\0';
7405 rc = VERR_BUFFER_OVERFLOW;
7406 }
7407 } while (0);
7408
7409 if (RT_UNLIKELY(fLockRead))
7410 {
7411 rc2 = vdThreadFinishRead(pDisk);
7412 AssertRC(rc2);
7413 }
7414
7415 LogFlowFunc(("returns %Rrc, pszFilename=\"%s\"\n", rc, pszFilename));
7416 return rc;
7417}
7418
7419/**
7420 * Get the comment line of image in HDD container.
7421 *
7422 * @returns VBox status code.
7423 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7424 * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
7425 * @param pDisk Pointer to HDD container.
7426 * @param nImage Image number, counts from 0. 0 is always base image of container.
7427 * @param pszComment Where to store the comment string of image. NULL is ok.
7428 * @param cbComment The size of pszComment buffer. 0 is ok.
7429 */
7430VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
7431 char *pszComment, unsigned cbComment)
7432{
7433 int rc;
7434 int rc2;
7435 bool fLockRead = false;
7436
7437 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p cbComment=%u\n",
7438 pDisk, nImage, pszComment, cbComment));
7439 do
7440 {
7441 /* sanity check */
7442 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7443 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7444
7445 /* Check arguments. */
7446 AssertMsgBreakStmt(VALID_PTR(pszComment),
7447 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
7448 rc = VERR_INVALID_PARAMETER);
7449 AssertMsgBreakStmt(cbComment,
7450 ("cbComment=%u\n", cbComment),
7451 rc = VERR_INVALID_PARAMETER);
7452
7453 rc2 = vdThreadStartRead(pDisk);
7454 AssertRC(rc2);
7455 fLockRead = true;
7456
7457 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7458 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7459
7460 rc = pImage->Backend->pfnGetComment(pImage->pBackendData, pszComment,
7461 cbComment);
7462 } while (0);
7463
7464 if (RT_UNLIKELY(fLockRead))
7465 {
7466 rc2 = vdThreadFinishRead(pDisk);
7467 AssertRC(rc2);
7468 }
7469
7470 LogFlowFunc(("returns %Rrc, pszComment=\"%s\"\n", rc, pszComment));
7471 return rc;
7472}
7473
7474/**
7475 * Changes the comment line of image in HDD container.
7476 *
7477 * @returns VBox status code.
7478 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7479 * @param pDisk Pointer to HDD container.
7480 * @param nImage Image number, counts from 0. 0 is always base image of container.
7481 * @param pszComment New comment string (UTF-8). NULL is allowed to reset the comment.
7482 */
7483VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
7484 const char *pszComment)
7485{
7486 int rc;
7487 int rc2;
7488 bool fLockWrite = false;
7489
7490 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p \"%s\"\n",
7491 pDisk, nImage, pszComment, pszComment));
7492 do
7493 {
7494 /* sanity check */
7495 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7496 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7497
7498 /* Check arguments. */
7499 AssertMsgBreakStmt(VALID_PTR(pszComment) || pszComment == NULL,
7500 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
7501 rc = VERR_INVALID_PARAMETER);
7502
7503 rc2 = vdThreadStartWrite(pDisk);
7504 AssertRC(rc2);
7505 fLockWrite = true;
7506
7507 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7508 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7509
7510 rc = pImage->Backend->pfnSetComment(pImage->pBackendData, pszComment);
7511 } while (0);
7512
7513 if (RT_UNLIKELY(fLockWrite))
7514 {
7515 rc2 = vdThreadFinishWrite(pDisk);
7516 AssertRC(rc2);
7517 }
7518
7519 LogFlowFunc(("returns %Rrc\n", rc));
7520 return rc;
7521}
7522
7523
7524/**
7525 * Get UUID 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 pUuid Where to store the image creation UUID.
7532 */
7533VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
7534{
7535 int rc;
7536 int rc2;
7537 bool fLockRead = false;
7538
7539 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
7540 do
7541 {
7542 /* sanity check */
7543 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7544 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7545
7546 /* Check arguments. */
7547 AssertMsgBreakStmt(VALID_PTR(pUuid),
7548 ("pUuid=%#p\n", pUuid),
7549 rc = VERR_INVALID_PARAMETER);
7550
7551 rc2 = vdThreadStartRead(pDisk);
7552 AssertRC(rc2);
7553 fLockRead = true;
7554
7555 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7556 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7557
7558 rc = pImage->Backend->pfnGetUuid(pImage->pBackendData, pUuid);
7559 } while (0);
7560
7561 if (RT_UNLIKELY(fLockRead))
7562 {
7563 rc2 = vdThreadFinishRead(pDisk);
7564 AssertRC(rc2);
7565 }
7566
7567 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
7568 return rc;
7569}
7570
7571/**
7572 * Set the image's UUID. Should not be used by normal applications.
7573 *
7574 * @returns VBox status code.
7575 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7576 * @param pDisk Pointer to HDD container.
7577 * @param nImage Image number, counts from 0. 0 is always base image of container.
7578 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
7579 */
7580VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
7581{
7582 int rc;
7583 int rc2;
7584 bool fLockWrite = false;
7585
7586 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
7587 pDisk, nImage, pUuid, pUuid));
7588 do
7589 {
7590 /* sanity check */
7591 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7592 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7593
7594 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
7595 ("pUuid=%#p\n", pUuid),
7596 rc = VERR_INVALID_PARAMETER);
7597
7598 rc2 = vdThreadStartWrite(pDisk);
7599 AssertRC(rc2);
7600 fLockWrite = true;
7601
7602 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7603 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7604
7605 RTUUID Uuid;
7606 if (!pUuid)
7607 {
7608 RTUuidCreate(&Uuid);
7609 pUuid = &Uuid;
7610 }
7611 rc = pImage->Backend->pfnSetUuid(pImage->pBackendData, pUuid);
7612 } while (0);
7613
7614 if (RT_UNLIKELY(fLockWrite))
7615 {
7616 rc2 = vdThreadFinishWrite(pDisk);
7617 AssertRC(rc2);
7618 }
7619
7620 LogFlowFunc(("returns %Rrc\n", rc));
7621 return rc;
7622}
7623
7624/**
7625 * Get last modification UUID of image in HDD container.
7626 *
7627 * @returns VBox status code.
7628 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7629 * @param pDisk Pointer to HDD container.
7630 * @param nImage Image number, counts from 0. 0 is always base image of container.
7631 * @param pUuid Where to store the image modification UUID.
7632 */
7633VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
7634{
7635 int rc = VINF_SUCCESS;
7636 int rc2;
7637 bool fLockRead = false;
7638
7639 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
7640 do
7641 {
7642 /* sanity check */
7643 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7644 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7645
7646 /* Check arguments. */
7647 AssertMsgBreakStmt(VALID_PTR(pUuid),
7648 ("pUuid=%#p\n", pUuid),
7649 rc = VERR_INVALID_PARAMETER);
7650
7651 rc2 = vdThreadStartRead(pDisk);
7652 AssertRC(rc2);
7653 fLockRead = true;
7654
7655 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7656 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7657
7658 rc = pImage->Backend->pfnGetModificationUuid(pImage->pBackendData,
7659 pUuid);
7660 } while (0);
7661
7662 if (RT_UNLIKELY(fLockRead))
7663 {
7664 rc2 = vdThreadFinishRead(pDisk);
7665 AssertRC(rc2);
7666 }
7667
7668 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
7669 return rc;
7670}
7671
7672/**
7673 * Set the image's last modification UUID. Should not be used by normal applications.
7674 *
7675 * @returns VBox status code.
7676 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7677 * @param pDisk Pointer to HDD container.
7678 * @param nImage Image number, counts from 0. 0 is always base image of container.
7679 * @param pUuid New modification UUID of the image. If NULL, a new UUID is created.
7680 */
7681VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
7682{
7683 int rc;
7684 int rc2;
7685 bool fLockWrite = false;
7686
7687 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
7688 pDisk, nImage, pUuid, pUuid));
7689 do
7690 {
7691 /* sanity check */
7692 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7693 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7694
7695 /* Check arguments. */
7696 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
7697 ("pUuid=%#p\n", pUuid),
7698 rc = VERR_INVALID_PARAMETER);
7699
7700 rc2 = vdThreadStartWrite(pDisk);
7701 AssertRC(rc2);
7702 fLockWrite = true;
7703
7704 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7705 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7706
7707 RTUUID Uuid;
7708 if (!pUuid)
7709 {
7710 RTUuidCreate(&Uuid);
7711 pUuid = &Uuid;
7712 }
7713 rc = pImage->Backend->pfnSetModificationUuid(pImage->pBackendData,
7714 pUuid);
7715 } while (0);
7716
7717 if (RT_UNLIKELY(fLockWrite))
7718 {
7719 rc2 = vdThreadFinishWrite(pDisk);
7720 AssertRC(rc2);
7721 }
7722
7723 LogFlowFunc(("returns %Rrc\n", rc));
7724 return rc;
7725}
7726
7727/**
7728 * Get parent UUID of image in HDD container.
7729 *
7730 * @returns VBox status code.
7731 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7732 * @param pDisk Pointer to HDD container.
7733 * @param nImage Image number, counts from 0. 0 is always base image of container.
7734 * @param pUuid Where to store the parent image UUID.
7735 */
7736VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
7737 PRTUUID pUuid)
7738{
7739 int rc = VINF_SUCCESS;
7740 int rc2;
7741 bool fLockRead = false;
7742
7743 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
7744 do
7745 {
7746 /* sanity check */
7747 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7748 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7749
7750 /* Check arguments. */
7751 AssertMsgBreakStmt(VALID_PTR(pUuid),
7752 ("pUuid=%#p\n", pUuid),
7753 rc = VERR_INVALID_PARAMETER);
7754
7755 rc2 = vdThreadStartRead(pDisk);
7756 AssertRC(rc2);
7757 fLockRead = true;
7758
7759 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7760 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7761
7762 rc = pImage->Backend->pfnGetParentUuid(pImage->pBackendData, pUuid);
7763 } while (0);
7764
7765 if (RT_UNLIKELY(fLockRead))
7766 {
7767 rc2 = vdThreadFinishRead(pDisk);
7768 AssertRC(rc2);
7769 }
7770
7771 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
7772 return rc;
7773}
7774
7775/**
7776 * Set the image's parent UUID. Should not be used by normal applications.
7777 *
7778 * @returns VBox status code.
7779 * @param pDisk Pointer to HDD container.
7780 * @param nImage Image number, counts from 0. 0 is always base image of container.
7781 * @param pUuid New parent UUID of the image. If NULL, a new UUID is created.
7782 */
7783VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
7784 PCRTUUID pUuid)
7785{
7786 int rc;
7787 int rc2;
7788 bool fLockWrite = false;
7789
7790 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
7791 pDisk, nImage, pUuid, pUuid));
7792 do
7793 {
7794 /* sanity check */
7795 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7796 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7797
7798 /* Check arguments. */
7799 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
7800 ("pUuid=%#p\n", pUuid),
7801 rc = VERR_INVALID_PARAMETER);
7802
7803 rc2 = vdThreadStartWrite(pDisk);
7804 AssertRC(rc2);
7805 fLockWrite = true;
7806
7807 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7808 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7809
7810 RTUUID Uuid;
7811 if (!pUuid)
7812 {
7813 RTUuidCreate(&Uuid);
7814 pUuid = &Uuid;
7815 }
7816 rc = pImage->Backend->pfnSetParentUuid(pImage->pBackendData, pUuid);
7817 } while (0);
7818
7819 if (RT_UNLIKELY(fLockWrite))
7820 {
7821 rc2 = vdThreadFinishWrite(pDisk);
7822 AssertRC(rc2);
7823 }
7824
7825 LogFlowFunc(("returns %Rrc\n", rc));
7826 return rc;
7827}
7828
7829
7830/**
7831 * Debug helper - dumps all opened images in HDD container into the log file.
7832 *
7833 * @param pDisk Pointer to HDD container.
7834 */
7835VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk)
7836{
7837 int rc2;
7838 bool fLockRead = false;
7839
7840 do
7841 {
7842 /* sanity check */
7843 AssertPtrBreak(pDisk);
7844 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7845
7846 if (!pDisk->pInterfaceErrorCallbacks || !VALID_PTR(pDisk->pInterfaceErrorCallbacks->pfnMessage))
7847 pDisk->pInterfaceErrorCallbacks->pfnMessage = vdLogMessage;
7848
7849 rc2 = vdThreadStartRead(pDisk);
7850 AssertRC(rc2);
7851 fLockRead = true;
7852
7853 vdMessageWrapper(pDisk, "--- Dumping VD Disk, Images=%u\n", pDisk->cImages);
7854 for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
7855 {
7856 vdMessageWrapper(pDisk, "Dumping VD image \"%s\" (Backend=%s)\n",
7857 pImage->pszFilename, pImage->Backend->pszBackendName);
7858 pImage->Backend->pfnDump(pImage->pBackendData);
7859 }
7860 } while (0);
7861
7862 if (RT_UNLIKELY(fLockRead))
7863 {
7864 rc2 = vdThreadFinishRead(pDisk);
7865 AssertRC(rc2);
7866 }
7867}
7868
7869/**
7870 * Query if asynchronous operations are supported for this disk.
7871 *
7872 * @returns VBox status code.
7873 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7874 * @param pDisk Pointer to the HDD container.
7875 * @param nImage Image number, counts from 0. 0 is always base image of container.
7876 * @param pfAIOSupported Where to store if async IO is supported.
7877 */
7878VBOXDDU_DECL(int) VDImageIsAsyncIOSupported(PVBOXHDD pDisk, unsigned nImage, bool *pfAIOSupported)
7879{
7880 int rc = VINF_SUCCESS;
7881 int rc2;
7882 bool fLockRead = false;
7883
7884 LogFlowFunc(("pDisk=%#p nImage=%u pfAIOSupported=%#p\n", pDisk, nImage, pfAIOSupported));
7885 do
7886 {
7887 /* sanity check */
7888 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7889 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7890
7891 /* Check arguments. */
7892 AssertMsgBreakStmt(VALID_PTR(pfAIOSupported),
7893 ("pfAIOSupported=%#p\n", pfAIOSupported),
7894 rc = VERR_INVALID_PARAMETER);
7895
7896 rc2 = vdThreadStartRead(pDisk);
7897 AssertRC(rc2);
7898 fLockRead = true;
7899
7900 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7901 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7902
7903 if (pImage->Backend->uBackendCaps & VD_CAP_ASYNC)
7904 *pfAIOSupported = pImage->Backend->pfnIsAsyncIOSupported(pImage->pBackendData);
7905 else
7906 *pfAIOSupported = false;
7907 } while (0);
7908
7909 if (RT_UNLIKELY(fLockRead))
7910 {
7911 rc2 = vdThreadFinishRead(pDisk);
7912 AssertRC(rc2);
7913 }
7914
7915 LogFlowFunc(("returns %Rrc, fAIOSupported=%u\n", rc, *pfAIOSupported));
7916 return rc;
7917}
7918
7919
7920VBOXDDU_DECL(int) VDAsyncRead(PVBOXHDD pDisk, uint64_t uOffset, size_t cbRead,
7921 PCRTSGBUF pcSgBuf,
7922 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
7923 void *pvUser1, void *pvUser2)
7924{
7925 int rc = VERR_VD_BLOCK_FREE;
7926 int rc2;
7927 bool fLockRead = false;
7928 PVDIOCTX pIoCtx = NULL;
7929
7930 LogFlowFunc(("pDisk=%#p uOffset=%llu pcSgBuf=%#p cbRead=%zu pvUser1=%#p pvUser2=%#p\n",
7931 pDisk, uOffset, pcSgBuf, cbRead, pvUser1, pvUser2));
7932
7933 do
7934 {
7935 /* sanity check */
7936 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7937 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7938
7939 /* Check arguments. */
7940 AssertMsgBreakStmt(cbRead,
7941 ("cbRead=%zu\n", cbRead),
7942 rc = VERR_INVALID_PARAMETER);
7943 AssertMsgBreakStmt(VALID_PTR(pcSgBuf),
7944 ("pcSgBuf=%#p\n", pcSgBuf),
7945 rc = VERR_INVALID_PARAMETER);
7946
7947 rc2 = vdThreadStartRead(pDisk);
7948 AssertRC(rc2);
7949 fLockRead = true;
7950
7951 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
7952 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
7953 uOffset, cbRead, pDisk->cbSize),
7954 rc = VERR_INVALID_PARAMETER);
7955
7956 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_READ, uOffset,
7957 cbRead, pcSgBuf,
7958 pfnComplete, pvUser1, pvUser2,
7959 NULL, vdReadHelperAsync);
7960 if (!pIoCtx)
7961 {
7962 rc = VERR_NO_MEMORY;
7963 break;
7964 }
7965
7966 pIoCtx->pImage = pDisk->pLast;
7967 AssertPtrBreakStmt(pIoCtx->pImage, rc = VERR_VD_NOT_OPENED);
7968
7969 rc = vdIoCtxProcess(pIoCtx);
7970 if (rc == VINF_VD_ASYNC_IO_FINISHED)
7971 {
7972 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
7973 vdIoCtxFree(pDisk, pIoCtx);
7974 else
7975 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
7976 }
7977 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
7978 vdIoCtxFree(pDisk, pIoCtx);
7979
7980 } while (0);
7981
7982 if (RT_UNLIKELY(fLockRead) && ( rc == VINF_VD_ASYNC_IO_FINISHED
7983 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
7984 {
7985 rc2 = vdThreadFinishRead(pDisk);
7986 AssertRC(rc2);
7987 }
7988
7989 LogFlowFunc(("returns %Rrc\n", rc));
7990 return rc;
7991}
7992
7993
7994VBOXDDU_DECL(int) VDAsyncWrite(PVBOXHDD pDisk, uint64_t uOffset, size_t cbWrite,
7995 PCRTSGBUF pcSgBuf,
7996 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
7997 void *pvUser1, void *pvUser2)
7998{
7999 int rc;
8000 int rc2;
8001 bool fLockWrite = false;
8002 PVDIOCTX pIoCtx = NULL;
8003
8004 LogFlowFunc(("pDisk=%#p uOffset=%llu cSgBuf=%#p cbWrite=%zu pvUser1=%#p pvUser2=%#p\n",
8005 pDisk, uOffset, pcSgBuf, cbWrite, pvUser1, pvUser2));
8006 do
8007 {
8008 /* sanity check */
8009 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8010 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8011
8012 /* Check arguments. */
8013 AssertMsgBreakStmt(cbWrite,
8014 ("cbWrite=%zu\n", cbWrite),
8015 rc = VERR_INVALID_PARAMETER);
8016 AssertMsgBreakStmt(VALID_PTR(pcSgBuf),
8017 ("pcSgBuf=%#p\n", pcSgBuf),
8018 rc = VERR_INVALID_PARAMETER);
8019
8020 rc2 = vdThreadStartWrite(pDisk);
8021 AssertRC(rc2);
8022 fLockWrite = true;
8023
8024 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
8025 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
8026 uOffset, cbWrite, pDisk->cbSize),
8027 rc = VERR_INVALID_PARAMETER);
8028
8029 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_WRITE, uOffset,
8030 cbWrite, pcSgBuf,
8031 pfnComplete, pvUser1, pvUser2,
8032 NULL, vdWriteHelperAsync);
8033 if (!pIoCtx)
8034 {
8035 rc = VERR_NO_MEMORY;
8036 break;
8037 }
8038
8039 PVDIMAGE pImage = pDisk->pLast;
8040 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
8041 pIoCtx->pImage = pImage;
8042
8043 rc = vdIoCtxProcess(pIoCtx);
8044 if (rc == VINF_VD_ASYNC_IO_FINISHED)
8045 {
8046 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
8047 vdIoCtxFree(pDisk, pIoCtx);
8048 else
8049 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
8050 }
8051 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
8052 vdIoCtxFree(pDisk, pIoCtx);
8053 } while (0);
8054
8055 if (RT_UNLIKELY(fLockWrite) && ( rc == VINF_VD_ASYNC_IO_FINISHED
8056 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
8057 {
8058 rc2 = vdThreadFinishWrite(pDisk);
8059 AssertRC(rc2);
8060 }
8061
8062 LogFlowFunc(("returns %Rrc\n", rc));
8063 return rc;
8064}
8065
8066
8067VBOXDDU_DECL(int) VDAsyncFlush(PVBOXHDD pDisk, PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
8068 void *pvUser1, void *pvUser2)
8069{
8070 int rc;
8071 int rc2;
8072 bool fLockWrite = false;
8073 PVDIOCTX pIoCtx = NULL;
8074
8075 LogFlowFunc(("pDisk=%#p\n", pDisk));
8076
8077 do
8078 {
8079 /* sanity check */
8080 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8081 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8082
8083 rc2 = vdThreadStartWrite(pDisk);
8084 AssertRC(rc2);
8085 fLockWrite = true;
8086
8087 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_FLUSH, 0,
8088 0, NULL,
8089 pfnComplete, pvUser1, pvUser2,
8090 NULL, vdFlushHelperAsync);
8091 if (!pIoCtx)
8092 {
8093 rc = VERR_NO_MEMORY;
8094 break;
8095 }
8096
8097 PVDIMAGE pImage = pDisk->pLast;
8098 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
8099 pIoCtx->pImage = pImage;
8100
8101 rc = vdIoCtxProcess(pIoCtx);
8102 if (rc == VINF_VD_ASYNC_IO_FINISHED)
8103 {
8104 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
8105 vdIoCtxFree(pDisk, pIoCtx);
8106 else
8107 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
8108 }
8109 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
8110 vdIoCtxFree(pDisk, pIoCtx);
8111 } while (0);
8112
8113 if (RT_UNLIKELY(fLockWrite) && ( rc == VINF_VD_ASYNC_IO_FINISHED
8114 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
8115 {
8116 rc2 = vdThreadFinishWrite(pDisk);
8117 AssertRC(rc2);
8118 }
8119
8120 LogFlowFunc(("returns %Rrc\n", rc));
8121 return rc;
8122}
8123
Note: See TracBrowser for help on using the repository browser.

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