VirtualBox

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

Last change on this file since 32519 was 32519, checked in by vboxsync, 15 years ago

Build fix

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

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