VirtualBox

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

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

src/*: more spelling fixes (logging), thanks Timeless!

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

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