VirtualBox

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

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

VD: Beginnings of the L2 disk cache

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