VirtualBox

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

Last change on this file since 38563 was 38563, checked in by vboxsync, 13 years ago

Storage: Add an almost working QCOW backend, can handle version 1 images. Support for QCOW2 is incomplete

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

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