VirtualBox

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

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

*: Fixes for incorrect RTStrAPrintf usage (it does NOT return an IPRT status code) and the odd bugs nearby.

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