VirtualBox

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

最後變更 在這個檔案從33360是 33355,由 vboxsync 提交於 14 年 前

Storage/VBoxHDD: make diff/base image creation fail with an appropriate error if the backend cannot handle this

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

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