VirtualBox

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

最後變更 在這個檔案從37119是 37119,由 vboxsync 提交於 14 年 前

VD: Fix rare hangs during I/O with flat VMDK images

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

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