VirtualBox

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

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

VD: Fix lock owner when unlocking the disk

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

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