VirtualBox

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

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

Storage: Implement offical support for other disk types like DVD and floppy images. DMG images can be used now without hacks

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

© 2023 Oracle
ContactPrivacy policyTerms of Use