VirtualBox

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

Last change on this file since 44242 was 44242, checked in by vboxsync, 12 years ago

Storage: Next step in the sync/async I/O unification, change all calls to pfnRead/pfnWrite/pfnFlush to pfnAsyncRead/pfnAsyncWrite/pfnFlush

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

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