VirtualBox

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

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

Devices: pass down the destination interface pointer

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

© 2023 Oracle
ContactPrivacy policyTerms of Use